read-cache: fix leaking hashfile when writing index fails
In `do_write_index()`, we use a `struct hashfile` to write the index with a trailer hash. In case the write fails though, we never clean up the allocated `hashfile` state and thus leak memory. Refactor the code to have a common exit path where we can free this and other allocated memory. While at it, refactor our use of `strbuf`s such that we reuse the same buffer to avoid some unneeded allocations. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:

committed by
Junio C Hamano

parent
c81dcf630c
commit
d1c53f6703
97
read-cache.c
97
read-cache.c
@ -2840,8 +2840,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
int csum_fsync_flag;
|
int csum_fsync_flag;
|
||||||
int ieot_entries = 1;
|
int ieot_entries = 1;
|
||||||
struct index_entry_offset_table *ieot = NULL;
|
struct index_entry_offset_table *ieot = NULL;
|
||||||
int nr, nr_threads;
|
|
||||||
struct repository *r = istate->repo;
|
struct repository *r = istate->repo;
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
int nr, nr_threads, ret;
|
||||||
|
|
||||||
f = hashfd(tempfile->fd, tempfile->filename.buf);
|
f = hashfd(tempfile->fd, tempfile->filename.buf);
|
||||||
|
|
||||||
@ -2962,8 +2963,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
strbuf_release(&previous_name_buf);
|
strbuf_release(&previous_name_buf);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
free(ieot);
|
ret = err;
|
||||||
return err;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = hashfile_total(f);
|
offset = hashfile_total(f);
|
||||||
@ -2985,20 +2986,20 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
* index.
|
* index.
|
||||||
*/
|
*/
|
||||||
if (ieot) {
|
if (ieot) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
write_ieot_extension(&sb, ieot);
|
write_ieot_extension(&sb, ieot);
|
||||||
err = write_index_ext_header(f, eoie_c, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0;
|
err = write_index_ext_header(f, eoie_c, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
free(ieot);
|
ret = -1;
|
||||||
if (err)
|
goto out;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
|
if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
|
||||||
istate->split_index) {
|
istate->split_index) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
if (istate->sparse_index)
|
if (istate->sparse_index)
|
||||||
die(_("cannot write split index for a sparse index"));
|
die(_("cannot write split index for a sparse index"));
|
||||||
@ -3007,59 +3008,66 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
write_index_ext_header(f, eoie_c, CACHE_EXT_LINK,
|
write_index_ext_header(f, eoie_c, CACHE_EXT_LINK,
|
||||||
sb.len) < 0;
|
sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
if (err)
|
ret = -1;
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
|
if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
|
||||||
!drop_cache_tree && istate->cache_tree) {
|
!drop_cache_tree && istate->cache_tree) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
cache_tree_write(&sb, istate->cache_tree);
|
cache_tree_write(&sb, istate->cache_tree);
|
||||||
err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;
|
err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
if (err)
|
ret = -1;
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
|
if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
|
||||||
istate->resolve_undo) {
|
istate->resolve_undo) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
resolve_undo_write(&sb, istate->resolve_undo);
|
resolve_undo_write(&sb, istate->resolve_undo);
|
||||||
err = write_index_ext_header(f, eoie_c, CACHE_EXT_RESOLVE_UNDO,
|
err = write_index_ext_header(f, eoie_c, CACHE_EXT_RESOLVE_UNDO,
|
||||||
sb.len) < 0;
|
sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
if (err)
|
ret = -1;
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
|
if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
|
||||||
istate->untracked) {
|
istate->untracked) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
write_untracked_extension(&sb, istate->untracked);
|
write_untracked_extension(&sb, istate->untracked);
|
||||||
err = write_index_ext_header(f, eoie_c, CACHE_EXT_UNTRACKED,
|
err = write_index_ext_header(f, eoie_c, CACHE_EXT_UNTRACKED,
|
||||||
sb.len) < 0;
|
sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
if (err)
|
ret = -1;
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
|
if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
|
||||||
istate->fsmonitor_last_update) {
|
istate->fsmonitor_last_update) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
write_fsmonitor_extension(&sb, istate);
|
write_fsmonitor_extension(&sb, istate);
|
||||||
err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0;
|
err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
if (err)
|
ret = -1;
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (istate->sparse_index) {
|
if (istate->sparse_index) {
|
||||||
if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0)
|
if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0) {
|
||||||
return -1;
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3069,14 +3077,15 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
* when loading the shared index.
|
* when loading the shared index.
|
||||||
*/
|
*/
|
||||||
if (eoie_c) {
|
if (eoie_c) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
strbuf_reset(&sb);
|
||||||
|
|
||||||
write_eoie_extension(&sb, eoie_c, offset);
|
write_eoie_extension(&sb, eoie_c, offset);
|
||||||
err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0;
|
err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0;
|
||||||
hashwrite(f, sb.buf, sb.len);
|
hashwrite(f, sb.buf, sb.len);
|
||||||
strbuf_release(&sb);
|
if (err) {
|
||||||
if (err)
|
ret = -1;
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
csum_fsync_flag = 0;
|
csum_fsync_flag = 0;
|
||||||
@ -3085,13 +3094,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
|
|
||||||
finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX,
|
finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX,
|
||||||
CSUM_HASH_IN_STREAM | csum_fsync_flag);
|
CSUM_HASH_IN_STREAM | csum_fsync_flag);
|
||||||
|
f = NULL;
|
||||||
|
|
||||||
if (close_tempfile_gently(tempfile)) {
|
if (close_tempfile_gently(tempfile)) {
|
||||||
error(_("could not close '%s'"), get_tempfile_path(tempfile));
|
ret = error(_("could not close '%s'"), get_tempfile_path(tempfile));
|
||||||
return -1;
|
goto out;
|
||||||
|
}
|
||||||
|
if (stat(get_tempfile_path(tempfile), &st)) {
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
if (stat(get_tempfile_path(tempfile), &st))
|
|
||||||
return -1;
|
|
||||||
istate->timestamp.sec = (unsigned int)st.st_mtime;
|
istate->timestamp.sec = (unsigned int)st.st_mtime;
|
||||||
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||||
trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
|
trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
|
||||||
@ -3105,7 +3117,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
|||||||
trace2_data_intmax("index", the_repository, "write/cache_nr",
|
trace2_data_intmax("index", the_repository, "write/cache_nr",
|
||||||
istate->cache_nr);
|
istate->cache_nr);
|
||||||
|
|
||||||
return 0;
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (f)
|
||||||
|
free_hashfile(f);
|
||||||
|
strbuf_release(&sb);
|
||||||
|
free(ieot);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_alternate_index_output(const char *name)
|
void set_alternate_index_output(const char *name)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
test_description='test handling of bogus index entries'
|
test_description='test handling of bogus index entries'
|
||||||
|
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
test_expect_success 'create tree with null sha1' '
|
test_expect_success 'create tree with null sha1' '
|
||||||
|
@ -5,6 +5,7 @@ test_description='basic update-index tests
|
|||||||
Tests for command-line parsing and basic operation.
|
Tests for command-line parsing and basic operation.
|
||||||
'
|
'
|
||||||
|
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
test_expect_success 'update-index --nonsense fails' '
|
test_expect_success 'update-index --nonsense fails' '
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
test_description='filter-branch removal of trees with null sha1'
|
test_description='filter-branch removal of trees with null sha1'
|
||||||
|
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
test_expect_success 'setup: base commits' '
|
test_expect_success 'setup: base commits' '
|
||||||
|
Reference in New Issue
Block a user