Properly check `strtol` for errors We are now using a custom `strtol` implementation to make sure we're not missing any overflow errors.
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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
diff --git a/include/git2/common.h b/include/git2/common.h
index e0c6bc5..22c7cc4 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -164,6 +164,12 @@
/** A reference with this name already exists */
#define GIT_EEXISTS (GIT_ERROR - 23)
+/** The given integer literal is too large to be parsed */
+#define GIT_EOVERFLOW (GIT_ERROR - 24)
+
+/** The given literal is not a valid number */
+#define GIT_ENOTNUM (GIT_ERROR - 25)
+
GIT_BEGIN_DECL
typedef struct {
diff --git a/src/errors.c b/src/errors.c
index 3c0e8eb..c3a495c 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -29,7 +29,9 @@ static struct {
{GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"},
{GIT_EINVALIDREFSTATE, "The state of the reference is not valid"},
{GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"},
- {GIT_EEXISTS, "A reference with this name already exists"}
+ {GIT_EEXISTS, "A reference with this name already exists"},
+ {GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
+ {GIT_ENOTNUM, "The given literal is not a valid number"},
};
const char *git_strerror(int num)
diff --git a/src/index.c b/src/index.c
index 6a31dd5..68bb9e2 100644
--- a/src/index.c
+++ b/src/index.c
@@ -411,6 +411,7 @@ static git_index_tree *read_tree_internal(
{
git_index_tree *tree;
const char *name_start, *buffer;
+ long count;
if ((tree = git__malloc(sizeof(git_index_tree))) == NULL)
return NULL;
@@ -429,12 +430,22 @@ static git_index_tree *read_tree_internal(
goto error_cleanup;
/* Blank-terminated ASCII decimal number of entries in this tree */
- tree->entries = strtol(buffer, (char **)&buffer, 10);
+ if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
+ count < 0)
+ goto error_cleanup;
+
+ tree->entries = (size_t)count;
+
if (*buffer != ' ' || ++buffer >= buffer_end)
goto error_cleanup;
/* Number of children of the tree, newline-terminated */
- tree->children_count = strtol(buffer, (char **)&buffer, 10);
+ if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
+ count < 0)
+ goto error_cleanup;
+
+ tree->children_count = (size_t)count;
+
if (*buffer != '\n' || ++buffer >= buffer_end)
goto error_cleanup;
diff --git a/src/revwalk.c b/src/revwalk.c
index 73bb060..b62b099 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -191,6 +191,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
unsigned char *parents_start;
int i, parents = 0;
+ long commit_time;
buffer += STRLEN("tree ") + GIT_OID_HEXSZ + 1;
@@ -227,10 +228,10 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
if (buffer == NULL)
return GIT_EOBJCORRUPTED;
- commit->time = strtol((char *)buffer + 2, NULL, 10);
- if (commit->time == 0)
+ if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
+ commit->time = (time_t)commit_time;
commit->parsed = 1;
return GIT_SUCCESS;
}
diff --git a/src/signature.c b/src/signature.c
index 0c99755..7c43979 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -66,13 +66,13 @@ git_signature *git_signature_dup(const git_signature *sig)
}
-static int parse_timezone_offset(const char *buffer, int *offset_out)
+static int parse_timezone_offset(const char *buffer, long *offset_out)
{
- int offset, dec_offset;
+ long offset, dec_offset;
int mins, hours;
- const char* offset_start;
- char* offset_end;
+ const char *offset_start;
+ const char *offset_end;
offset_start = buffer + 1;
@@ -84,7 +84,8 @@ static int parse_timezone_offset(const char *buffer, int *offset_out)
if (offset_start[0] != '-' && offset_start[0] != '+')
return GIT_EOBJCORRUPTED;
- dec_offset = strtol(offset_start + 1, &offset_end, 10);
+ if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
+ return GIT_EOBJCORRUPTED;
if (offset_end - offset_start != 5)
return GIT_EOBJCORRUPTED;
@@ -117,7 +118,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
int name_length, email_length;
const char *buffer = *buffer_out;
const char *line_end, *name_end, *email_end;
- int offset = 0;
+ long offset = 0, time;
memset(sig, 0x0, sizeof(git_signature));
@@ -159,11 +160,11 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
if (buffer >= line_end)
return GIT_EOBJCORRUPTED;
- sig->when.time = strtol(buffer, (char **)&buffer, 10);
-
- if (sig->when.time == 0)
+ if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
+ sig->when.time = (time_t)time;
+
if (parse_timezone_offset(buffer, &offset) < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
diff --git a/src/tree.c b/src/tree.c
index b474d39..64f81d7 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -150,7 +150,8 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS)
return GIT_ENOMEM;
- entry->attr = strtol(buffer, (char **)&buffer, 8);
+ if (git__strtol32((long *)&entry->attr, buffer, &buffer, 8) < GIT_SUCCESS)
+ return GIT_EOBJCORRUPTED;
if (*buffer++ != ' ') {
error = GIT_EOBJCORRUPTED;
diff --git a/src/util.c b/src/util.c
index 995daf3..55a7ab2 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2,6 +2,7 @@
#include "common.h"
#include <stdarg.h>
#include <stdio.h>
+#include <ctype.h>
void git_strarray_free(git_strarray *array)
{
@@ -12,6 +13,84 @@ void git_strarray_free(git_strarray *array)
free(array->strings);
}
+int git__strtol32(long *result, const char *nptr, const char **endptr, int base)
+{
+ const char *p;
+ long n, nn;
+ int c, ovfl, v, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ while (isspace(*p))
+ p++;
+
+ /*
+ * Sign
+ */
+ if (*p == '-' || *p == '+')
+ if (*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if (base == 0) {
+ if (*p != '0')
+ base = 10;
+ else {
+ base = 8;
+ if (p[1] == 'x' || p[1] == 'X') {
+ p += 2;
+ base = 16;
+ }
+ }
+ } else if (base == 16 && *p == '0') {
+ if (p[1] == 'x' || p[1] == 'X')
+ p += 2;
+ } else if (base < 0 || 36 < base)
+ goto Return;
+
+ /*
+ * Non-empty sequence of digits
+ */
+ for (;; p++,ndig++) {
+ c = *p;
+ v = base;
+ if ('0'<=c && c<='9')
+ v = c - '0';
+ else if ('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else if ('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if (v >= base)
+ break;
+ nn = n*base + v;
+ if (nn < n)
+ ovfl = 1;
+ n = nn;
+ }
+
+Return:
+ if (ndig == 0)
+ return GIT_ENOTNUM;
+
+ if (endptr)
+ *endptr = p;
+
+ if (ovfl)
+ return GIT_EOVERFLOW;
+
+ *result = neg ? -n : n;
+ return GIT_SUCCESS;
+}
+
int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...)
{
va_list va;
diff --git a/src/util.h b/src/util.h
index f477b64..3c60649 100644
--- a/src/util.h
+++ b/src/util.h
@@ -22,6 +22,8 @@ extern int git__fmt(char *, size_t, const char *, ...)
extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
+extern int git__strtol32(long *n, const char *buff, const char **end_buf, int base);
+
/*
* The dirname() function shall take a pointer to a character string
* that contains a pathname, and return a pointer to a string that is a