From 9734b74a8f9e327e02762024596961209cf989d5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 30 Jan 2020 04:52:19 -0500 Subject: [PATCH 1/4] normalize_path_copy(): document "dst" size expectations We take a "dst" buffer to write into, but there's no matching "len" parameter. The hidden assumption is that normalizing always makes things smaller, so we're OK as long as "dst" is at least as big as "src". Let's document that explicitly. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/path.c b/path.c index a76eec8b96..88cf593007 100644 --- a/path.c +++ b/path.c @@ -1077,6 +1077,8 @@ const char *remove_leading_path(const char *in, const char *prefix) /* * It is okay if dst == src, but they should not overlap otherwise. + * The "dst" buffer must be at least as long as "src"; normalizing may shrink + * the size of the path, but will never grow it. * * Performs the following normalizations on src, storing the result in dst: * - Ensures that components are separated by '/' (Windows only) From 667b76ec5819b151355e322ad9c6264b9cb3f9cd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 30 Jan 2020 04:52:32 -0500 Subject: [PATCH 2/4] walker_fetch(): avoid raw array length computation We compute the length of an array of object_id's with a raw multiplication. In theory this could trigger an integer overflow which would cause an under-allocation (and eventually an out of bounds write). I doubt this can be triggered in practice, since you'd need to feed it an enormous number of target objects, which would typically come from the ref advertisement and be using proportional memory. And even on 64-bit systems, where "int" is much smaller than "size_t", that should hold: even though "targets" is an int, the multiplication will be done as a size_t because of the use of sizeof(). But we can easily fix it by using ALLOC_ARRAY(), which uses st_mult() under the hood. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- walker.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/walker.c b/walker.c index 06cd2bd569..bb010f7a2b 100644 --- a/walker.c +++ b/walker.c @@ -261,12 +261,14 @@ int walker_fetch(struct walker *walker, int targets, char **target, struct strbuf refname = STRBUF_INIT; struct strbuf err = STRBUF_INIT; struct ref_transaction *transaction = NULL; - struct object_id *oids = xmalloc(targets * sizeof(struct object_id)); + struct object_id *oids; char *msg = NULL; int i, ret = -1; save_commit_buffer = 0; + ALLOC_ARRAY(oids, targets); + if (write_ref) { transaction = ref_transaction_begin(&err); if (!transaction) { From 8dd40c0472a8f51f4fba4b8fd28dc3e9421cd7d7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 30 Jan 2020 04:53:38 -0500 Subject: [PATCH 3/4] traverse_trees(): use stack array for name entries We heap-allocate our arrays of name_entry structs, etc, with one entry per tree we're asked to traverse. The code does a raw multiplication in the xmalloc() call, which I find when auditing for integer overflows during allocation. We could "fix" this by using ALLOC_ARRAY() instead. But as it turns out, the maximum size of these arrays is limited at compile time: - merge_trees() always passes in 3 trees - unpack_trees() and its brethren never pass in more than MAX_UNPACK_TREES So we can simplify even further by just using a stack array and bounding it with MAX_UNPACK_TREES. There should be no concern with overflowing the stack, since MAX_UNPACK_TREES is only 8 and the structs themselves are small. Note that since we're replacing xcalloc(), we have to move one of the NULL initializations into a loop. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- tree-walk.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tree-walk.c b/tree-walk.c index d5a8e096a6..3093cf7098 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -410,15 +410,20 @@ int traverse_trees(struct index_state *istate, struct traverse_info *info) { int error = 0; - struct name_entry *entry = xmalloc(n*sizeof(*entry)); + struct name_entry entry[MAX_UNPACK_TREES]; int i; - struct tree_desc_x *tx = xcalloc(n, sizeof(*tx)); + struct tree_desc_x tx[ARRAY_SIZE(entry)]; struct strbuf base = STRBUF_INIT; int interesting = 1; char *traverse_path; - for (i = 0; i < n; i++) + if (n >= ARRAY_SIZE(entry)) + BUG("traverse_trees() called with too many trees (%d)", n); + + for (i = 0; i < n; i++) { tx[i].d = t[i]; + tx[i].skip = NULL; + } if (info->prev) { strbuf_make_traverse_path(&base, info->prev, @@ -506,10 +511,8 @@ int traverse_trees(struct index_state *istate, if (mask & (1ul << i)) update_extended_entry(tx + i, entry + i); } - free(entry); for (i = 0; i < n; i++) free_extended_entry(tx + i); - free(tx); free(traverse_path); info->traverse_path = NULL; strbuf_release(&base); From 5290d4513496d89f84570985a0e02e97dff477ff Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 1 Feb 2020 06:39:22 -0500 Subject: [PATCH 4/4] tree-walk.c: break circular dependency with unpack-trees The unpack-trees API depends on the tree-walk API. But we've recently introduced a dependency in tree-walk.c on MAX_UNPACK_TREES, which doesn't otherwise care about unpack-trees at all. Let's break that dependency by reversing the constants: we'll introduce a new MAX_TRAVERSE_TREES which belongs to the tree-walk API. And then we can define MAX_UNPACK_TREES in terms of that (since unpack-trees cannot possibly work with more trees than it can traverse at once via tree-walk). The value for both will remain at 8. This is somewhat arbitrary and probably more than is necessary, per ca885a4fe6 (read-tree() and unpack_trees(): use consistent limit, 2008-03-13), but there's not really any pressing need to reduce it. Suggested-by: Elijah Newren Signed-off-by: Jeff King Acked-by: Elijah Newren Signed-off-by: Junio C Hamano --- tree-walk.c | 3 +-- tree-walk.h | 2 ++ unpack-trees.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tree-walk.c b/tree-walk.c index 3093cf7098..bb0ad34c54 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -1,6 +1,5 @@ #include "cache.h" #include "tree-walk.h" -#include "unpack-trees.h" #include "dir.h" #include "object-store.h" #include "tree.h" @@ -410,7 +409,7 @@ int traverse_trees(struct index_state *istate, struct traverse_info *info) { int error = 0; - struct name_entry entry[MAX_UNPACK_TREES]; + struct name_entry entry[MAX_TRAVERSE_TREES]; int i; struct tree_desc_x tx[ARRAY_SIZE(entry)]; struct strbuf base = STRBUF_INIT; diff --git a/tree-walk.h b/tree-walk.h index 826396c8ed..a5058469e9 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -3,6 +3,8 @@ #include "cache.h" +#define MAX_TRAVERSE_TREES 8 + /** * The tree walking API is used to traverse and inspect trees. */ diff --git a/unpack-trees.h b/unpack-trees.h index ca94a421a5..ae1557fb80 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -6,7 +6,7 @@ #include "string-list.h" #include "tree-walk.h" -#define MAX_UNPACK_TREES 8 +#define MAX_UNPACK_TREES MAX_TRAVERSE_TREES struct cache_entry; struct unpack_trees_options;