Skip to contentSkip to conversation

builtin/init-db: preemptively clear repo_fmt to avoid leaks

PATCH In review Andrzej Hunt via GitGitGadget 3 participants 4 messages May 5, 2021 – May 6, 2021

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 builtin/init-db: preemptively clear repo_fmt to avoid leaks

From: Andrzej Hunt <[email protected]>

init_db() could populate various fields on repo_fmt, we add a clear_repo_fmt() call to guarantee that we won't leak any of repo_fmt's members.

At this time, we do not allocate any new data into repo_fmt, but that changes in the following patch that is currently in seen:
https://lore.kernel.org/git/2fd7cb8c0983501e2af2f195aec81d7c17fb80e1.1618832277.git.gitgitgadget@gmail.com/
Because it adds the following allocation in init_db():
repo_fmt.ref_storage = xstrdup(ref_storage_format);

The patch linked to above is only exposing a preexisting problem - we can add clear_repository_format() independently to preemptively avoid this class of leak entirely.

Leaks in init_db() are of little significance as this function is called immediately at the end of cmd_init_db(). However a leak in init_db() will cause all known leak-free tests (t0000+1+5) to start failing when run with LSAN - preemptively plugging such leaks is therefore helpful to avoid regressing on those tests, and more significantly helps the ongoing efforts to get all of t0000-t0099 to run leak-free.

LSAN output from t0000 on seen:

Direct leak of 6 byte(s) in 1 object(s) allocated from:

    #0 0x4868b4 in strdup ../projects/compiler-rt/lib/asan/asan_interceptors.cpp:452:3
    #1 0xaa1218 in xstrdup wrapper.c:29:14
    #2 0x5a4b0a in init_db builtin/init-db.c:442:25
    #3 0x5a7a17 in cmd_init_db builtin/init-db.c:742:9
    #4 0x4ce8fe in run_builtin git.c:461:11
    #5 0x4ccbc8 in handle_builtin git.c:718:3
    #6 0x4cb0cc in run_argv git.c:785:4
    #7 0x4cb0cc in cmd_main git.c:916:19
    #8 0x6bf63d in main common-main.c:52:11
    #9 0x7f3222f5f349 in __libc_start_main (/lib64/libc.so.6+0x24349)

SUMMARY: AddressSanitizer: 6 byte(s) leaked in 1 allocation(s).

---
builtin/init-db: preemptively plug repo_fmt leak

This patch preemptively plugs a leak which is currently only occuring in seen in hn/reftable.

This patch is orthogonal to that series (that series starts allocating new memory into an already existing struct - and my patch only adds a clear call for that struct - in other words my patch is safe to use independent of hn/reftable). But there's also no good reason to use this patch independently of that series since we're not leaking prior to that series. I'm not sure what the optimal logistics with my patch are, feel free to integrate it into that series and/or to fold my change into the relevant commit if that's easiest!

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1018%2Fahunt%2Finit_db_leak-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1018/ahunt/init_db_leak-v1
Pull-Request: https://github.com/git/git/pull/1018

 builtin/init-db.c | 1 +
 1 file changed, 1 insertion(+)
builtin/init-db.c +1 -0
index 31b718259a64..b25147ebaf59 100644
@@ -512,6 +512,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 	}
 
+	clear_repository_format(&repo_fmt);
 	free(original_git_dir);
 	return 0;
 }

base-commit: c3901c8daf043cdc16ffb20d3a410f3a2d5494fd

Signature · 1 line
gitgitgadget
#1 lore raw

Hi Andrzej,

builtin/init-db.c int init_db(const char *git_dir, const char *real_git_dir, · line 515

Quoted 9 lines · Andrzej Hunt via GitGitGadget
builtin/init-db.c +1 -0
index 31b718259a64..b25147ebaf59 100644
@@ -512,6 +512,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 	}
 
+	clear_repository_format(&repo_fmt);

I am afraid that this might not be correct, as t0410.27 experienced a segmentation fault (see
https://github.com/git/git/pull/1018/checks?check_run_id=2511749719#step:5:2845
for the full details):

-- snip --
[...]
+ repack_and_check -a bd6a66906d425462f8fbf1e3a61cda48cc4e456c
3251618c5193c77083052fb7d6ed5e8b0b227a94
+ rm -rf repo2
+ cp -r repo repo2
+ git -C repo2 repack -a -d
warning: reflog of 'HEAD' references pruned commits
warning: reflog of 'refs/heads/master' references pruned commits Segmentation fault (core dumped)
error: last command exited with $?=139
not ok 27 - repack -d does not irreversibly delete promisor objects -- snap --

When I encountered similar problems with my patches in the past, I found the --valgrind-only option incredibly useful, i.e. something like

	cd t && sh t0410-*.sh -i -v -x --valgrind-only=27

Maybe you can give it a try?

Ciao,
Dscho

Quoted 9 lines
builtin/init-db.c +0 -0
 	free(original_git_dir);
 	return 0;
 }

base-commit: c3901c8daf043cdc16ffb20d3a410f3a2d5494fd --
gitgitgadget

#2 lore raw

Hi Johannes,

Quoted 12 lines · Johannes Schindelin

Hi Andrzej,

Quoted 8 lines · Andrzej Hunt via GitGitGadget
builtin/init-db.c +0 -0
index 31b718259a64..b25147ebaf59 100644
@@ -512,6 +512,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
  			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
  	}
 
Quoted 6 lines

builtin/init-db.c int init_db(const char *git_dir, const char *real_git_dir, · line 515

Quoted 1 line
builtin/init-db.c +1 -0
+	clear_repository_format(&repo_fmt);

I am afraid that this might not be correct, as t0410.27 experienced a segmentation fault (see
https://github.com/git/git/pull/1018/checks?check_run_id=2511749719#step:5:2845
for the full details):

Thanks for spotting that. On further investigation this looks like a preexisting issue on seen (my github PR was based on seen - in hindsight that was probably not a good idea) - here's a CI run from seen this morning exhibiting the same failure:
https://github.com/git/git/runs/2515095446?check_suite_focus=true

To gain more confidence, I've rebased my patch onto next, and I no longer hit any CI failures:
https://github.com/ahunt/git/runs/2515309312?check_suite_focus=true
(I ran this on my fork because changing the base branch appears to have confused the PR's CI configuration.)

Nevertheless, I was still curious about what might be causing the failure in the first place: it appears to only happen in CI with gcc on Linux, and I was not able to repro on my machine using (tested with gcc-7.5 and gcc-10, with seen from this morning):
make CC=gcc-10 T=t0410-partial-clone.sh test

I'm guessing that understanding this failure might require the help of someone with an Ubuntu install to debug (unless there's some easy way of using Github's Actions to run a bisect)?

-- snip --

ATB,

Andrzej

#3 lore raw

Hi Andrzej,

Quoted 27 lines · Andrzej Hunt
Quoted 11 lines · Johannes Schindelin
Quoted 9 lines · Andrzej Hunt via GitGitGadget
builtin/init-db.c +0 -0
index 31b718259a64..b25147ebaf59 100644
@@ -512,6 +512,7 @@ int init_db(const char *git_dir, const char
 *real_git_dir,
   		       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
   }
 
Quoted 6 lines

builtin/init-db.c int init_db(const char *git_dir, const char *real_git_dir, · line 515

Quoted 1 line
builtin/init-db.c +1 -0
+	clear_repository_format(&repo_fmt);

I am afraid that this might not be correct, as t0410.27 experienced a segmentation fault (see
https://github.com/git/git/pull/1018/checks?check_run_id=2511749719#step:5:2845
for the full details):

Thanks for spotting that. On further investigation this looks like a preexisting issue on seen (my github PR was based on seen - in hindsight that was probably not a good idea) - here's a CI run from seen this morning exhibiting the same failure:
https://github.com/git/git/runs/2515095446?check_suite_focus=true

Quoted 5 lines

To gain more confidence, I've rebased my patch onto next, and I no longer hit any CI failures:
https://github.com/ahunt/git/runs/2515309312?check_suite_focus=true
(I ran this on my fork because changing the base branch appears to have confused the PR's CI configuration.)

Oh, I'm sorry! But it is great news that this is not _your_ patch's problem (I had a brief look over it and wondered why it would cause this segfault).

Quoted 5 lines

Nevertheless, I was still curious about what might be causing the failure in the first place: it appears to only happen in CI with gcc on Linux, and I was not able to repro on my machine using (tested with gcc-7.5 and gcc-10, with seen from this morning):
make CC=gcc-10 T=t0410-partial-clone.sh test

There are more settings used in the CI run, indeed. So yeah, it would be good to debug this in an environment where it does fail.

Quoted 3 lines

I'm guessing that understanding this failure might require the help of someone with an Ubuntu install to debug (unless there's some easy way of using Github's Actions to run a bisect)?

As a matter of fact, there is a neat way in GitHub Actions to debug this:
the action-tmate Action. It installs and starts a tmate instance, which is a tmux inside kind of a proxied SSH server.

This is what I usually do when I have time to track down such problems: I edit .github/workflows/main.yml and push it into a branch in my fork. The edits are essentially:

- I delete the ci-config job (because I know that I want to run this workflow and don't want to wait for that job to be queued and run)

- Obviously, this requires all the needs: ci-config and
if: needs.ci-config.outputs.enabled == 'yes' lines to be deleted, too.

- I also delete the jobs, tasks, and matrix elements that are not necessary for my debugging session (in your case, that would be e.g. the dockerized job as well as the vectors in regular that are not linux-gcc).

- Then comes the most important part: after the ci/print-test-failures.sh task, I insert this task:

- name: action-tmate
if: failure()
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true

The limit-access-to-actor thing only applies if you uploaded your public SSH key to your GitHub profile. Otherwise, anybody who monitors the run could see the command-line to connect to your debugging instance and interfere with you.

For details, see https://mxschmitt.github.io/action-tmate/

Thanks,
Dscho