Skip to content

Commit 5aa02f9

Browse files
peffgitster
authored andcommitted
tree-walk: harden make_traverse_path() length computations
The make_traverse_path() function isn't very careful about checking its output buffer boundaries. In fact, it doesn't even _know_ the size of the buffer it's writing to, and just assumes that the caller used traverse_path_len() correctly. And even then we assume that our traverse_info.pathlen components are all correct, and just blindly write into the buffer. Let's improve this situation a bit: - have the caller pass in their allocated buffer length, which we'll check against our own computations - check for integer underflow as we do our backwards-insertion of pathnames into the buffer - check that we do not run out items in our list to traverse before we've filled the expected number of bytes None of these should be triggerable in practice (especially since our switch to size_t everywhere in a previous commit), but it doesn't hurt to check our assumptions. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent c43ab06 commit 5aa02f9

File tree

3 files changed

+23
-10
lines changed

3 files changed

+23
-10
lines changed

tree-walk.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,32 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
181181
info->prev = &dummy;
182182
}
183183

184-
char *make_traverse_path(char *path, const struct traverse_info *info,
184+
char *make_traverse_path(char *path, size_t pathlen,
185+
const struct traverse_info *info,
185186
const char *name, size_t namelen)
186187
{
187-
size_t pathlen = info->pathlen;
188+
/* Always points to the end of the name we're about to add */
189+
size_t pos = st_add(info->pathlen, namelen);
188190

189-
path[pathlen + namelen] = 0;
191+
if (pos >= pathlen)
192+
BUG("too small buffer passed to make_traverse_path");
193+
194+
path[pos] = 0;
190195
for (;;) {
191-
memcpy(path + pathlen, name, namelen);
192-
if (!pathlen)
196+
if (pos < namelen)
197+
BUG("traverse_info pathlen does not match strings");
198+
pos -= namelen;
199+
memcpy(path + pos, name, namelen);
200+
201+
if (!pos)
193202
break;
194-
path[--pathlen] = '/';
203+
path[--pos] = '/';
204+
205+
if (!info)
206+
BUG("traverse_info ran out of list items");
195207
name = info->name;
196208
namelen = info->namelen;
197209
info = info->prev;
198-
pathlen -= namelen;
199210
}
200211
return path;
201212
}
@@ -207,7 +218,8 @@ void strbuf_make_traverse_path(struct strbuf *out,
207218
size_t len = traverse_path_len(info, namelen);
208219

209220
strbuf_grow(out, len);
210-
make_traverse_path(out->buf + out->len, info, name, namelen);
221+
make_traverse_path(out->buf + out->len, out->alloc - out->len,
222+
info, name, namelen);
211223
strbuf_setlen(out, out->len + len);
212224
}
213225

tree-walk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct traverse_info {
7070
};
7171

7272
int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *);
73-
char *make_traverse_path(char *path, const struct traverse_info *info,
73+
char *make_traverse_path(char *path, size_t pathlen, const struct traverse_info *info,
7474
const char *name, size_t namelen);
7575
void strbuf_make_traverse_path(struct strbuf *out,
7676
const struct traverse_info *info,

unpack-trees.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,8 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
968968
ce->ce_flags = create_ce_flags(stage);
969969
ce->ce_namelen = len;
970970
oidcpy(&ce->oid, &n->oid);
971-
make_traverse_path(ce->name, info, n->path, n->pathlen);
971+
/* len+1 because the cache_entry allocates space for NUL */
972+
make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen);
972973

973974
return ce;
974975
}

0 commit comments

Comments
 (0)