From: Johannes Schindelin <johannes.schindelin@xxxxxx> In f9e6c649589e (untracked cache: load from UNTR index extension, 2015-03-08), code was added to read back the untracked cache from an index extension. Probably in the endeavor to avoid the `calloc()` implied by `FLEX_ALLOC_STR()` (it is hard to know why exactly, the commit message of that commit is a bit parsimonious with information), it calls `malloc()` manually and then `memcpy()`s the bits and pieces into place. It allocates the size of `struct untracked_cache_dir` plus the string length of the untracked file name, then copies the information in two steps: first the fixed-size metadata, then the name. And here lies the rub: it includes the trailing NUL byte in the name. If `FLEX_ARRAY` is defined as 0, this results in a buffer overrun. To fix this, let's just add 1, for the trailing NUL byte. Technically, this overallocates on platforms where `FLEX_ARRAY` is 1, but it should not matter much in reality, as `malloc()` usually overallocates anyway, unless the size to allocate aligns exactly with some internal chunk size (see below for more on that). The real strange thing is that neither valgrind nor DrMemory catches this bug. In this developer's tests, a `memcpy()` (but not a `memset()`!) could write up to 4 bytes after the allocated memory range before valgrind would start reporting an issue. However, when running Git built with nedmalloc as allocator, under rare conditions (and inconsistently at that), this bug triggered an `abort()` because nedmalloc rounds up the size to be `malloc()`ed to a multiple of a certain chunk size, then adds a few bytes to be used for storing some internal state. If there is no rounding up to do (because the size is already a multiple of that chunk size), and if the buffer is overrun as in the code patched in this commit, the internal state is corrupted. The scenario that triggered this here bug fix entailed a git.git checkout with an extra copy of the source code in an untracked subdirectory, meaning that there was an untracked subdirectory called "thunderbird-patch-inline" whose name's length is exactly 24 bytes, which, added to the size of above-mentioned `struct untracked_cache_dir` that weighs in with 104 bytes on a 64-bit system, amounts to 128, aligning perfectly with nedmalloc's chunk size. As there is no obvious way to trigger this bug reliably, on all platforms supported by Git, and as the bug is obvious enough, this patch comes without a regression test. Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> --- dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dir.c b/dir.c index b2cabadf25..f5293a6536 100644 --- a/dir.c +++ b/dir.c @@ -2760,7 +2760,7 @@ static int read_one_dir(struct untracked_cache_dir **untracked_, next = data + len + 1; if (next > rd->end) return -1; - *untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len)); + *untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), len, 1)); memcpy(untracked, &ud, sizeof(ud)); memcpy(untracked->name, data, len + 1); data = next; -- gitgitgadget