Skip to content

Commit d1c53f6

Browse files
pks-tgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c81dcf6 commit d1c53f6

File tree

4 files changed

+62
-39
lines changed

4 files changed

+62
-39
lines changed

read-cache.c

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,8 +2840,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
28402840
int csum_fsync_flag;
28412841
int ieot_entries = 1;
28422842
struct index_entry_offset_table *ieot = NULL;
2843-
int nr, nr_threads;
28442843
struct repository *r = istate->repo;
2844+
struct strbuf sb = STRBUF_INIT;
2845+
int nr, nr_threads, ret;
28452846

28462847
f = hashfd(tempfile->fd, tempfile->filename.buf);
28472848

@@ -2962,8 +2963,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
29622963
strbuf_release(&previous_name_buf);
29632964

29642965
if (err) {
2965-
free(ieot);
2966-
return err;
2966+
ret = err;
2967+
goto out;
29672968
}
29682969

29692970
offset = hashfile_total(f);
@@ -2985,20 +2986,20 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
29852986
* index.
29862987
*/
29872988
if (ieot) {
2988-
struct strbuf sb = STRBUF_INIT;
2989+
strbuf_reset(&sb);
29892990

29902991
write_ieot_extension(&sb, ieot);
29912992
err = write_index_ext_header(f, eoie_c, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0;
29922993
hashwrite(f, sb.buf, sb.len);
2993-
strbuf_release(&sb);
2994-
free(ieot);
2995-
if (err)
2996-
return -1;
2994+
if (err) {
2995+
ret = -1;
2996+
goto out;
2997+
}
29972998
}
29982999

29993000
if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
30003001
istate->split_index) {
3001-
struct strbuf sb = STRBUF_INIT;
3002+
strbuf_reset(&sb);
30023003

30033004
if (istate->sparse_index)
30043005
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,
30073008
write_index_ext_header(f, eoie_c, CACHE_EXT_LINK,
30083009
sb.len) < 0;
30093010
hashwrite(f, sb.buf, sb.len);
3010-
strbuf_release(&sb);
3011-
if (err)
3012-
return -1;
3011+
if (err) {
3012+
ret = -1;
3013+
goto out;
3014+
}
30133015
}
30143016
if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
30153017
!drop_cache_tree && istate->cache_tree) {
3016-
struct strbuf sb = STRBUF_INIT;
3018+
strbuf_reset(&sb);
30173019

30183020
cache_tree_write(&sb, istate->cache_tree);
30193021
err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;
30203022
hashwrite(f, sb.buf, sb.len);
3021-
strbuf_release(&sb);
3022-
if (err)
3023-
return -1;
3023+
if (err) {
3024+
ret = -1;
3025+
goto out;
3026+
}
30243027
}
30253028
if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
30263029
istate->resolve_undo) {
3027-
struct strbuf sb = STRBUF_INIT;
3030+
strbuf_reset(&sb);
30283031

30293032
resolve_undo_write(&sb, istate->resolve_undo);
30303033
err = write_index_ext_header(f, eoie_c, CACHE_EXT_RESOLVE_UNDO,
30313034
sb.len) < 0;
30323035
hashwrite(f, sb.buf, sb.len);
3033-
strbuf_release(&sb);
3034-
if (err)
3035-
return -1;
3036+
if (err) {
3037+
ret = -1;
3038+
goto out;
3039+
}
30363040
}
30373041
if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
30383042
istate->untracked) {
3039-
struct strbuf sb = STRBUF_INIT;
3043+
strbuf_reset(&sb);
30403044

30413045
write_untracked_extension(&sb, istate->untracked);
30423046
err = write_index_ext_header(f, eoie_c, CACHE_EXT_UNTRACKED,
30433047
sb.len) < 0;
30443048
hashwrite(f, sb.buf, sb.len);
3045-
strbuf_release(&sb);
3046-
if (err)
3047-
return -1;
3049+
if (err) {
3050+
ret = -1;
3051+
goto out;
3052+
}
30483053
}
30493054
if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
30503055
istate->fsmonitor_last_update) {
3051-
struct strbuf sb = STRBUF_INIT;
3056+
strbuf_reset(&sb);
30523057

30533058
write_fsmonitor_extension(&sb, istate);
30543059
err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0;
30553060
hashwrite(f, sb.buf, sb.len);
3056-
strbuf_release(&sb);
3057-
if (err)
3058-
return -1;
3061+
if (err) {
3062+
ret = -1;
3063+
goto out;
3064+
}
30593065
}
30603066
if (istate->sparse_index) {
3061-
if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0)
3062-
return -1;
3067+
if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0) {
3068+
ret = -1;
3069+
goto out;
3070+
}
30633071
}
30643072

30653073
/*
@@ -3069,14 +3077,15 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
30693077
* when loading the shared index.
30703078
*/
30713079
if (eoie_c) {
3072-
struct strbuf sb = STRBUF_INIT;
3080+
strbuf_reset(&sb);
30733081

30743082
write_eoie_extension(&sb, eoie_c, offset);
30753083
err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0;
30763084
hashwrite(f, sb.buf, sb.len);
3077-
strbuf_release(&sb);
3078-
if (err)
3079-
return -1;
3085+
if (err) {
3086+
ret = -1;
3087+
goto out;
3088+
}
30803089
}
30813090

30823091
csum_fsync_flag = 0;
@@ -3085,13 +3094,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
30853094

30863095
finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX,
30873096
CSUM_HASH_IN_STREAM | csum_fsync_flag);
3097+
f = NULL;
30883098

30893099
if (close_tempfile_gently(tempfile)) {
3090-
error(_("could not close '%s'"), get_tempfile_path(tempfile));
3091-
return -1;
3100+
ret = error(_("could not close '%s'"), get_tempfile_path(tempfile));
3101+
goto out;
3102+
}
3103+
if (stat(get_tempfile_path(tempfile), &st)) {
3104+
ret = -1;
3105+
goto out;
30923106
}
3093-
if (stat(get_tempfile_path(tempfile), &st))
3094-
return -1;
30953107
istate->timestamp.sec = (unsigned int)st.st_mtime;
30963108
istate->timestamp.nsec = ST_MTIME_NSEC(st);
30973109
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,
31053117
trace2_data_intmax("index", the_repository, "write/cache_nr",
31063118
istate->cache_nr);
31073119

3108-
return 0;
3120+
ret = 0;
3121+
3122+
out:
3123+
if (f)
3124+
free_hashfile(f);
3125+
strbuf_release(&sb);
3126+
free(ieot);
3127+
return ret;
31093128
}
31103129

31113130
void set_alternate_index_output(const char *name)

t/t1601-index-bogus.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/bin/sh
22

33
test_description='test handling of bogus index entries'
4+
5+
TEST_PASSES_SANITIZE_LEAK=true
46
. ./test-lib.sh
57

68
test_expect_success 'create tree with null sha1' '

t/t2107-update-index-basic.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='basic update-index tests
55
Tests for command-line parsing and basic operation.
66
'
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011
test_expect_success 'update-index --nonsense fails' '

t/t7008-filter-branch-null-sha1.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
test_description='filter-branch removal of trees with null sha1'
44

5+
TEST_PASSES_SANITIZE_LEAK=true
56
. ./test-lib.sh
67

78
test_expect_success 'setup: base commits' '

0 commit comments

Comments
 (0)