summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2019-04-10 05:56:48 -0700
committerJunio C Hamano <gitster@pobox.com>2019-04-12 10:49:40 +0900
commit3a7b45a62360ff0e14f7150ee2d9930c0c258dbd (patch)
treec69d6379f829e61a8d26c2e736dcf392b89e8c62
parentaeb582a98374c094361cba1bd756dc6307432c42 (diff)
downloadgit-3a7b45a62360ff0e14f7150ee2d9930c0c258dbd.tar.gz
untracked cache: fix off-by-one
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@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--dir.c2
1 files changed, 1 insertions, 1 deletions
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;