Introduce git__add_sizet_overflow and friends Add some helper functions to check for overflow in a type-specific manner.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
diff --git a/src/common.h b/src/common.h
index 8b7d693..530e320 100644
--- a/src/common.h
+++ b/src/common.h
@@ -58,6 +58,7 @@
#include "git2/types.h"
#include "git2/errors.h"
#include "thread-utils.h"
+#include "integer.h"
#include <regex.h>
@@ -174,13 +175,14 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
GITERR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
+
/** Check for integer overflow from addition or multiplication */
#define GIT_ALLOC_OVERFLOW_ADD(one, two) \
- (((one) + (two) < (one)) ? (giterr_set_oom(), 1) : 0)
+ (!git__add_sizet_overflow(NULL, (one), (two)) ? (giterr_set_oom(), 1) : 0)
/** Check for integer overflow from multiplication */
#define GIT_ALLOC_OVERFLOW_MULTIPLY(one, two) \
- ((one && ((one) * (two)) / (one) != (two)) ? (giterr_set_oom(), 1) : 0)
+ (!git__multiply_sizet_overflow(NULL, (one), (two)) ? (giterr_set_oom(), 1) : 0)
/** Check for additive overflow, failing if it would occur. */
#define GITERR_CHECK_ALLOC_ADD(one, two) \
diff --git a/src/integer.h b/src/integer.h
new file mode 100644
index 0000000..a9ed10a
--- /dev/null
+++ b/src/integer.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_integer_h__
+#define INCLUDE_integer_h__
+
+/** @return true if p fits into the range of a size_t */
+GIT_INLINE(int) git__is_sizet(git_off_t p)
+{
+ size_t r = (size_t)p;
+ return p == (git_off_t)r;
+}
+
+/** @return true if p fits into the range of an ssize_t */
+GIT_INLINE(int) git__is_ssizet(size_t p)
+{
+ ssize_t r = (ssize_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of a uint32_t */
+GIT_INLINE(int) git__is_uint32(size_t p)
+{
+ uint32_t r = (uint32_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of an unsigned long */
+GIT_INLINE(int) git__is_ulong(git_off_t p)
+{
+ unsigned long r = (unsigned long)p;
+ return p == (git_off_t)r;
+}
+
+/**
+ * Sets `one + two` into `out`, unless the arithmetic would overflow.
+ * @return true if the result fits in a `uint64_t`, false on overflow.
+ */
+GIT_INLINE(bool) git__add_uint64_overflow(uint64_t *out, uint64_t one, uint64_t two)
+{
+ if (UINT64_MAX - one < two)
+ return false;
+ if (out)
+ *out = one + two;
+ return true;
+}
+
+/**
+ * Sets `one + two` into `out`, unless the arithmetic would overflow.
+ * @return true if the result fits in a `size_t`, false on overflow.
+ */
+GIT_INLINE(bool) git__add_sizet_overflow(size_t *out, size_t one, size_t two)
+{
+ if (SIZE_MAX - one < two)
+ return false;
+ if (out)
+ *out = one + two;
+ return true;
+}
+
+/**
+ * Sets `one * two` into `out`, unless the arithmetic would overflow.
+ * @return true if the result fits in a `size_t`, false on overflow.
+ */
+GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t two)
+{
+ if (one && SIZE_MAX / one < two)
+ return false;
+ if (out)
+ *out = one * two;
+ return true;
+}
+
+#endif /* INCLUDE_integer_h__ */
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 9b56234..8236ef9 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -833,8 +833,8 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,
trg_object->delta_data = NULL;
}
if (delta_cacheable(pb, src_size, trg_size, delta_size)) {
- GITERR_CHECK_ALLOC_ADD(pb->delta_cache_size, delta_size);
- pb->delta_cache_size += delta_size;
+ if (!git__add_uint64_overflow(&pb->delta_cache_size, pb->delta_cache_size, delta_size))
+ return -1;
git_packbuilder__cache_unlock(pb);
diff --git a/src/util.h b/src/util.h
index dcdaf43..86139ef 100644
--- a/src/util.h
+++ b/src/util.h
@@ -146,34 +146,6 @@ extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int
extern void git__hexdump(const char *buffer, size_t n);
extern uint32_t git__hash(const void *key, int len, uint32_t seed);
-/** @return true if p fits into the range of a size_t */
-GIT_INLINE(int) git__is_sizet(git_off_t p)
-{
- size_t r = (size_t)p;
- return p == (git_off_t)r;
-}
-
-/** @return true if p fits into the range of an ssize_t */
-GIT_INLINE(int) git__is_ssizet(size_t p)
-{
- ssize_t r = (ssize_t)p;
- return p == (size_t)r;
-}
-
-/** @return true if p fits into the range of a uint32_t */
-GIT_INLINE(int) git__is_uint32(size_t p)
-{
- uint32_t r = (uint32_t)p;
- return p == (size_t)r;
-}
-
-/** @return true if p fits into the range of an unsigned long */
-GIT_INLINE(int) git__is_ulong(git_off_t p)
-{
- unsigned long r = (unsigned long)p;
- return p == (git_off_t)r;
-}
-
/* 32-bit cross-platform rotl */
#ifdef _MSC_VER /* use built-in method in MSVC */
# define git__rotl(v, s) (uint32_t)_rotl(v, s)