name-hash: don't reuse cache_entry in dir_entry

Stop reusing cache_entry in dir_entry; doing so causes a
use-after-free bug.

During merges, we free entries that we no longer need in the
destination index.  But those entries might have also been stored in
the dir_entry cache, and when a later call to add_to_index found them,
they would be used after being freed.

To prevent this, change dir_entry to store a copy of the name instead
of a pointer to a cache_entry.  This entails some refactoring of code
that expects the cache_entry.

Keith McGuigan <kmcguigan@twitter.com> diagnosed this bug and wrote
the initial patch, but this version does not use any of Keith's code.

Helped-by: Keith McGuigan <kmcguigan@twitter.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
David Turner
2015-10-21 13:54:11 -04:00
committed by Junio C Hamano
parent 441c4a4017
commit 41284eb0f9
4 changed files with 35 additions and 60 deletions

View File

@ -661,21 +661,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
* entry's directory case.
*/
if (ignore_case) {
const char *startPtr = ce->name;
const char *ptr = startPtr;
while (*ptr) {
while (*ptr && *ptr != '/')
++ptr;
if (*ptr == '/') {
struct cache_entry *foundce;
++ptr;
foundce = index_dir_exists(istate, ce->name, ptr - ce->name - 1);
if (foundce) {
memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
startPtr = ptr;
}
}
}
adjust_dirname_case(istate, ce->name);
}
alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);