Skip to content

double free detected in tcache 2 #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
karlding opened this issue Nov 17, 2022 · 0 comments · Fixed by #70
Closed

double free detected in tcache 2 #69

karlding opened this issue Nov 17, 2022 · 0 comments · Fixed by #70
Labels
upstream Blocked on upstream libgit2/git2rs

Comments

@karlding
Copy link
Contributor

I'm not sure if this is the same issue described in #62 given there's no reproduction case available, but I encountered a similar segfault when using binaries built from the Cargo.lock file at the current HEAD (219e386ff665b4fd360b397752ce51a658c5e1d6). This is also the case with the latest prebuilt binaries from GitHub (git-absorb-0.6.7-x86_64-unknown-linux-musl.tar.gz).

Unfortunately I can't share the repository to reproduce this either, but I did manage to find a solution to my problem.

I was seeing a segmentation fault on a specific repository:

# in the repo with changes
$ git add -u .
$ git absorb
Segmentation fault (core dumped)

(Also apologies if I got to my conclusions in a roundabout way, this was my first time looking at Rust code, so do let me know if there's a better way of doing things)

Debugging

Since the prebuilt binaries don't have debug symbols, I tried building from source and ensured that things were still failing. This gave the following message:

# in the tummychow/git-absorb repo
$ cargo clean && cargo build --locked

# in the repo with changes
$ git absorb
free(): double free detected in tcache 2
Aborted (core dumped)

Running this under rust-gdb gives the following backtrace:

 >>> bt
 #0  0x00007ffff7dac00b in raise () from /lib/x86_64-linux-gnu/libc.so.6
 #1  0x00007ffff7d8b859 in abort () from /lib/x86_64-linux-gnu/libc.so.6
 #2  0x00007ffff7df626e in ?? () from /lib/x86_64-linux-gnu/libc.so.6
 #3  0x00007ffff7dfe2fc in ?? () from /lib/x86_64-linux-gnu/libc.so.6
 #4  0x00007ffff7dfff6d in ?? () from /lib/x86_64-linux-gnu/libc.so.6
 #5  0x000055555564fe5b in stdalloc__free (ptr=0x5555559ee2e0) at libgit2/src/allocators/stdalloc.c:104
 #6  0x0000555555611277 in git_mwindow_close_lru_window () at libgit2/src/mwindow.c:269
 #7  0x00005555556114c2 in new_window (fd=7, size=92396285398, offset=49063184399) at libgit2/src/mwindow.c:337
 #8  0x00005555556116df in git_mwindow_open (mwf=0x5555559d50e0, cursor=0x7fffffff1f58, offset=49063184399, extra=20, left=0x7fffffff1ee0) at libgit2/src/mwindow.c:407
 #9  0x000055555564c156 in git_packfile_unpack_header (size_p=0x7fffffff1f70, type_p=0x7fffffff1f4c, mwf=0x5555559d50e0, w_curs=0x7fffffff1f58, curpos=0x7fffffff1f60) at libgit2/src/pack.c:455
 #10 0x000055555564c6c2 in pack_dependency_chain (chain_out=0x7fffffff2040, cached_out=0x7fffffff2010, cached_off=0x7fffffff2920, small_stack=0x7fffffff20a0, stack_sz=0x7fffffff2018, p=0x5555559d50e0, obj_offset=49063184399) at libgit2/src/pack.c:581
 #11 0x000055555564c8bc in git_packfile_unpack (obj=0x7fffffff2900, p=0x5555559d50e0, obj_offset=0x7fffffff2920) at libgit2/src/pack.c:637
 #12 0x0000555555667e74 in pack_backend__read (buffer_p=0x7fffffff29b0, len_p=0x7fffffff29b8, type_p=0x7fffffff29c0, backend=0x5555559c4f90, oid=0x555555abb145) at libgit2/src/odb_pack.c:400
 #13 0x000055555564292d in odb_read_1 (out=0x7fffffff2a88, db=0x5555559ca3d0, id=0x555555abb145, only_refreshed=false) at libgit2/src/odb.c:1065
 #14 0x0000555555642b3c in git_odb_read (out=0x7fffffff2a88, db=0x5555559ca3d0, id=0x555555abb145) at libgit2/src/odb.c:1116
 #15 0x0000555555611ff5 in git_object_lookup_prefix (object_out=0x7fffffff2b50, repo=0x5555559b4fe0, id=0x555555abb145, len=40, type=GIT_OBJECT_TREE) at libgit2/src/object.c:222
 #16 0x00005555556120c7 in git_object_lookup (object_out=0x7fffffff2b50, repo=0x5555559b4fe0, id=0x555555abb145, type=GIT_OBJECT_TREE) at libgit2/src/object.c:253
 #17 0x00005555556097aa in git_tree_lookup (out=0x7fffffff2b50, repo=0x5555559b4fe0, id=0x555555abb145) at libgit2/src/object_api.c:56
 #18 0x000055555566ab6c in tree_iterator_frame_push (iter=0x5555559cdd70, entry=0x555555ab6c48) at libgit2/src/iterator.c:660
 #19 0x000055555566b130 in tree_iterator_advance (out=0x7fffffff2ca8, i=0x5555559cdd70) at libgit2/src/iterator.c:813
 #20 0x0000555555625006 in git_iterator_advance (entry=0x7fffffff2ca8, iter=0x5555559cdd70) at libgit2/src/iterator.h:184
 #21 0x000055555562739c in iterator_advance (entry=0x7fffffff2ca8, iterator=0x5555559cdd70) at libgit2/src/diff_generate.c:925
 #22 0x0000555555627aaf in handle_matched_item (diff=0x5555559cb200, info=0x7fffffff2c90) at libgit2/src/diff_generate.c:1179
 #23 0x0000555555627cf7 in git_diff__from_iterators (out=0x7fffffff2d20, repo=0x5555559b4fe0, old_iter=0x5555559cdd70, new_iter=0x5555559ca200, opts=0x7fffffff3730) at libgit2/src/diff_generate.c:1249
 #24 0x00005555556280a0 in git_diff_tree_to_tree (out=0x7fffffff2e08, repo=0x5555559b4fe0, old_tree=0x5555559d8f50, new_tree=0x5555559cc120, opts=0x7fffffff3730) at libgit2/src/diff_generate.c:1319
 #25 0x00005555555eb58e in git2::repo::Repository::diff_tree_to_tree (self=0x7fffffff34c0, old_tree=..., new_tree=..., opts=...) at src/repo.rs:2374
 #26 0x00005555555b5791 in git_absorb::run (config=0x7fffffffce00) at src/lib.rs:42
 #27 0x000055555559c416 in git_absorb::main () at src/main.rs:102

The backtrace was somewhere in libgit2, and I noticed that the pinned version was rather old. So the first thing I tried was updating the dependencies.

After updating dependencies via cargo update, the double free no longer occurs.

# in the tummychow/git-absorb repo
$ cargo clean && cargo update && cargo build --locked

$ git diff | grep -A3 'libgit2-sys'
name = "libgit2-sys"
-version = "0.12.13+1.0.1"
+version = "0.12.26+1.3.0"

# in the repo with changes
$ git absorb
(success)

This updated the transitive libgit2-sys dependency in the Cargo.lock file from 0.12.13+1.0.1 to 0.12.26+1.3.0. Since this works, it indicates that the issue I was seeing was fixed upstream already.

I then tried explicitly adding libgit2-sys as a dependency and setting an exact version to override the Cargo dependency resolver (without updating git2). The first libgit2-sys after the current version in the Cargo.lock file that fixes this seems to be =0.12.14+1.1.0.

$ nvim Cargo.toml
# add libgit2-sys = "=0.12.14+1.1.0" under [dependencies]

# regenerate the Cargo.lock file + rebuild

That release bumps the libgit2 dependency, so most likely some change in libgit2 between 1.0.0 and 1.1.0 (there's 335 commits here).

At this point, I was happy that things were working so I stopped looking further into this to see what commit in libgit2 fixes the issue.

TL; DR

Updating Cargo.lock to pull in a newer upstream libgit2 dependency seems to fix a double free

karlding added a commit to karlding/git-absorb that referenced this issue Nov 17, 2022
There is a double free in libgit2 that is fixed somewhere between
v1.0.0 and v1.1.0. This means that the transitive libgit2-sys
dependency needs to be after 0.12.14+1.1.0.

This manifests itself via the following error message on certain
repositories:

    $ git absorb
    free(): double free detected in tcache 2
    Aborted (core dumped)

Bump git2 to the latest version, which also updates the transitive
libgit2-sys (and libgit2) dependency.

Fixes tummychow#69
@tummychow tummychow added the upstream Blocked on upstream libgit2/git2rs label Apr 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
upstream Blocked on upstream libgit2/git2rs
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants