]> git.ipfire.org Git - thirdparty/git.git/commitdiff
read-cache: fix leaking hashfile when writing index fails
authorPatrick Steinhardt <ps@pks.im>
Wed, 14 Aug 2024 06:52:06 +0000 (08:52 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Aug 2024 17:07:58 +0000 (10:07 -0700)
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>
read-cache.c
t/t1601-index-bogus.sh
t/t2107-update-index-basic.sh
t/t7008-filter-branch-null-sha1.sh

index 48bf24f87c00c15e5a4b249b26eabc624546f901..36821fe5b5e19221b854c56bdb7fd22524d02387 100644 (file)
@@ -2840,8 +2840,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        int csum_fsync_flag;
        int ieot_entries = 1;
        struct index_entry_offset_table *ieot = NULL;
-       int nr, nr_threads;
        struct repository *r = istate->repo;
+       struct strbuf sb = STRBUF_INIT;
+       int nr, nr_threads, ret;
 
        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);
 
        if (err) {
-               free(ieot);
-               return err;
+               ret = err;
+               goto out;
        }
 
        offset = hashfile_total(f);
@@ -2985,20 +2986,20 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
         * index.
         */
        if (ieot) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                write_ieot_extension(&sb, ieot);
                err = write_index_ext_header(f, eoie_c, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               free(ieot);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
 
        if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
            istate->split_index) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                if (istate->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,
                                               sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
        if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
            !drop_cache_tree && istate->cache_tree) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                cache_tree_write(&sb, istate->cache_tree);
                err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
        if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
            istate->resolve_undo) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                resolve_undo_write(&sb, istate->resolve_undo);
                err = write_index_ext_header(f, eoie_c, CACHE_EXT_RESOLVE_UNDO,
                                             sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
        if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
            istate->untracked) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                write_untracked_extension(&sb, istate->untracked);
                err = write_index_ext_header(f, eoie_c, CACHE_EXT_UNTRACKED,
                                             sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
        if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
            istate->fsmonitor_last_update) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                write_fsmonitor_extension(&sb, istate);
                err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
        if (istate->sparse_index) {
-               if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0)
-                       return -1;
+               if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0) {
+                       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.
         */
        if (eoie_c) {
-               struct strbuf sb = STRBUF_INIT;
+               strbuf_reset(&sb);
 
                write_eoie_extension(&sb, eoie_c, offset);
                err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0;
                hashwrite(f, sb.buf, sb.len);
-               strbuf_release(&sb);
-               if (err)
-                       return -1;
+               if (err) {
+                       ret = -1;
+                       goto out;
+               }
        }
 
        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,
                          CSUM_HASH_IN_STREAM | csum_fsync_flag);
+       f = NULL;
 
        if (close_tempfile_gently(tempfile)) {
-               error(_("could not close '%s'"), get_tempfile_path(tempfile));
-               return -1;
+               ret = error(_("could not close '%s'"), get_tempfile_path(tempfile));
+               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.nsec = ST_MTIME_NSEC(st);
        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",
                           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)
index 4171f1e14103135ef3e47a3b3df19d5a285b4a8a..5dcc10188280b2c0ff4b6b9465c87651de47c5a8 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test handling of bogus index entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create tree with null sha1' '
index cc72ead79f397873b5005196a99baad6b0ba2450..f0eab13f96a6b946f982db2543112cc7467d857d 100755 (executable)
@@ -5,6 +5,7 @@ test_description='basic update-index tests
 Tests for command-line parsing and basic operation.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'update-index --nonsense fails' '
index 93fbc92b8dbc297512050846c4f00ec803dea4a9..0ce8fd2c895ddae895d6fd0bb7c531d1d712241c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='filter-branch removal of trees with null sha1'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '