`git diff-tree --cc` SEGFAUTs after adding trace_printf to diff_tree_combined. Details in attached git-bugreport.
Thank you for filling out a Git bug report! Please answer the following questions to help us understand your issue. What did you do before the bug happened? (Steps to reproduce your issue) I'm learning some inner workings of git so I've added `trace_printf` statements to the `diff_tree_combined` function and others. While running ./git I ran into a problem where `git diff-tree --cc` fails with a SEGFAULT. This bug report is a "minimal" change to `diff_tree_combined` of the `git@xxxxxxxxxx:git/git` repo that reproduces the problem. The change adds an additional for loop inside the loop that counts the number of "surving paths" and it prints the `struct combined_diff_path` fields so I can see the list of paths that make up merge commits with changes. Below is the diff which is applied to the `next` branch, it is also available on my fork on github: https://github.com/winksaville/git/tree/wink-segfault-with-minimal-changes ``` wink@fwlaptop 25-01-03T18:43:04.330Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) $ git --no-pager diff next diff --git a/combine-diff.c b/combine-diff.c index 641bc92dbd..455bc19087 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -20,6 +20,8 @@ #include "oid-array.h" #include "revision.h" +#include "trace.h" + static int compare_paths(const struct combine_diff_path *one, const struct diff_filespec *two) { @@ -1595,8 +1597,16 @@ void diff_tree_combined(const struct object_id *oid, } /* find out number of surviving paths */ - for (num_paths = 0, p = paths; p; p = p->next) + trace_printf("Wink diff_tree_combined: find number of surviving paths num_parent=%d\n", num_parent); + for (num_paths = 0, p = paths; p; p = p->next) { + trace_printf("Wink diff_tree_combined: num_paths=%d &p=%p mode=%0x, oid=%s path=%s\n", num_paths, p, p->mode, oid_to_hex(&p->oid), p->path); + for (i = 0; i < num_parent; i++) { + trace_printf("Wink diff_tree_combined: &p->parent[%d]=%p status=%c mode=%x oid=%s path.buf=%p contents path.buf=%s\n", + i, &p->parent[i], p->parent[i].status, p->parent[i].mode, oid_to_hex(&p->parent[i].oid), p->parent[i].path.buf, p->parent[i].path.buf); + } num_paths++; + } + trace_printf("Wink diff_tree_combined: found %d surviving paths\n", num_paths); /* order paths according to diffcore_order */ if (opt->orderfile && num_paths) { wink@fwlaptop 25-01-03T18:43:18.695Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) ``` I made those changes on the `next` branch of git/git repo as of yesterday, 1/2/25, here are the the relavant logs: ``` wink@fwlaptop 25-01-03T18:43:18.695Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) $ git log -2 --oneline edf34e3ab4 (HEAD -> wink-segfault-with-minimal-changes, origin/wink-segfault-with-minimal-changes) bug: SEGFAULT with minimal changes: 6c04ab211c (upstream/next, origin/next, next) Sync with 'master' wink@fwlaptop 25-01-03T18:44:13.976Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) ``` What did you expect to happen? (Expected behavior) When I use `git diff-tree --cc` on a merge commit with changes I expect to see my trace_printf statements print the `struct combined_diff_path` members and also the output diff-tree -cc results with the "combined changes" as can be seen below: ``` wink@fwlaptop 25-01-03T18:46:58.472Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) $ GIT_TRACE=1 ./git diff-tree --cc 6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a 10:47:04.259504 git.c:476 trace: built-in: git diff-tree --cc 6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a 6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a 10:47:04.278220 combine-diff.c:1600 Wink diff_tree_combined: find number of surviving paths num_parent=2 10:47:04.278245 combine-diff.c:1602 Wink diff_tree_combined: num_paths=0 &p=0x5dd590883f60 mode=81a4, oid=0f41b2fd4a6b679a1cfcaa9a584c382068146212 path=refs.c 10:47:04.278253 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[0]=0x5dd590883f98 status=M mode=81a4 oid=7dd5e9fa3323111f06303674b213ae24ed2d04b6 path.buf=(nil) contents path.buf=(null) 10:47:04.278260 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[1]=0x5dd590883fe0 status=M mode=81a4 oid=c55583986940d8ef1e1c839364c03cd92d4f7114 path.buf=(nil) contents path.buf=(null) 10:47:04.278265 combine-diff.c:1602 Wink diff_tree_combined: num_paths=1 &p=0x5dd59088b450 mode=81a4, oid=a0cdd99250e8286b55808b697b0a94afac5d8319 path=refs.h 10:47:04.278270 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[0]=0x5dd59088b488 status=M mode=81a4 oid=09be47afbee51e99f4ae49588cd65596ccfcb07e path.buf=(nil) contents path.buf=(null) 10:47:04.278274 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[1]=0x5dd59088b4d0 status=M mode=81a4 oid=b0dfc65ed2e59c4b66967840339f81e7746a96d3 path.buf=(nil) contents path.buf=(null) 10:47:04.278278 combine-diff.c:1602 Wink diff_tree_combined: num_paths=2 &p=0x5dd59088b530 mode=81a4, oid=5cfb8b7ca8678e171b8e8a7ad6daf1af74a81b59 path=refs/files-backend.c 10:47:04.278283 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[0]=0x5dd59088b568 status=M mode=81a4 oid=467fe347fa7e7d82ed7a2836e43ea749bb90ad7d path.buf=(nil) contents path.buf=(null) 10:47:04.278287 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[1]=0x5dd59088b5b0 status=M mode=81a4 oid=8953d1c6d37b13b0db701888b3db92fd87a68aaa path.buf=(nil) contents path.buf=(null) 10:47:04.278291 combine-diff.c:1602 Wink diff_tree_combined: num_paths=3 &p=0x5dd59088b620 mode=81a4, oid=16550862d3ebe3b357c52254088b143c7ba000d6 path=refs/refs-internal.h 10:47:04.278296 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[0]=0x5dd59088b658 status=M mode=81a4 oid=66e66e0fc1e812ebebd1d4b0119899c84bf1c0ae path.buf=(nil) contents path.buf=(null) 10:47:04.278300 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[1]=0x5dd59088b6a0 status=M mode=81a4 oid=79b287c5ec5c7d8f759869cf93cda405640186dc path.buf=(nil) contents path.buf=(null) 10:47:04.278305 combine-diff.c:1602 Wink diff_tree_combined: num_paths=4 &p=0x5dd59088b710 mode=81a4, oid=00d95a9a2f42ce74c5cb4a42175b0953287851a6 path=refs/reftable-backend.c 10:47:04.278309 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[0]=0x5dd59088b748 status=M mode=81a4 oid=8a2a5b847c3d86332e319da69bfb5c8a56a10e86 path.buf=(nil) contents path.buf=(null) 10:47:04.278314 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[1]=0x5dd59088b790 status=M mode=81a4 oid=bec5962debea7b62572d08f6fa8fd38ab4cd8af6 path.buf=(nil) contents path.buf=(null) 10:47:04.278318 combine-diff.c:1609 Wink diff_tree_combined: found 5 surviving paths diff --cc refs/files-backend.c index 467fe347fa,8953d1c6d3..5cfb8b7ca8 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@@ -2533,9 -2539,15 +2543,15 @@@ static int check_old_oid(struct ref_upd oid_to_hex(oid), oid_to_hex(&update->old_oid)); - return -1; + return ret; } + struct files_transaction_backend_data { + struct ref_transaction *packed_transaction; + int packed_refs_locked; + struct strmap ref_locks; + }; + /* * Prepare for carrying out update: * - Lock the reference referred to by update. wink@fwlaptop 25-01-03T18:47:04.295Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) ``` The above works because it has a hack fix I introduced and thus it works. The hack is to always use `find_paths_generic` rather than `find_paths_multitree`. To do this I added `need_generic_pathscan = true;` on top of the above change to have it run properly: ``` wink@fwlaptop 25-01-03T18:47:04.295Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) $ git diff diff --git a/combine-diff.c b/combine-diff.c index 455bc19087..f03ff6f820 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1563,7 +1563,7 @@ void diff_tree_combined(const struct object_id *oid, (opt->pickaxe_opts & (DIFF_PICKAXE_KINDS_MASK & ~DIFF_PICKAXE_KIND_OBJFIND)) || opt->filter; - + need_generic_pathscan = true; if (need_generic_pathscan) { /* * NOTE generic case also handles --stat, as it computes wink@fwlaptop 25-01-03T18:49:56.900Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) ``` What happened instead? (Actual behavior) If I don't use my hack a SEGFAULT occurs: ``` wink@fwlaptop 25-01-03T18:51:32.242Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) $ GIT_TRACE=1 ./git diff-tree --cc 6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a 10:51:37.211992 git.c:476 trace: built-in: git diff-tree --cc 6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a 6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a 10:51:37.216500 combine-diff.c:1600 Wink diff_tree_combined: find number of surviving paths num_parent=2 10:51:37.216516 combine-diff.c:1602 Wink diff_tree_combined: num_paths=0 &p=0x6325add91f60 mode=81a4, oid=0f41b2fd4a6b679a1cfcaa9a584c382068146212 path=refs.c 10:51:37.216524 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[0]=0x6325add91f98 status=M mode=81a4 oid=7dd5e9fa3323111f06303674b213ae24ed2d04b6 path.buf=(nil) contents path.buf=(null) 10:51:37.216530 combine-diff.c:1604 Wink diff_tree_combined: &p->parent[1]=0x6325add91fe0 status=M mode=81a4 oid=c55583986940d8ef1e1c839364c03cd92d4f7114 path.buf=0x632588e43a00 contents path.buf=�C%c 10:51:37.216536 combine-diff.c:1602 Wink diff_tree_combined: num_paths=1 &p=0x6325add990c0 mode=81a4, oid=a0cdd99250e8286b55808b697b0a94afac5d8319 path=refs.h Segmentation fault (core dumped) wink@fwlaptop 25-01-03T18:51:37.350Z:~/prgs/forks/git (wink-segfault-with-minimal-changes) ``` What's different between what you expected and what actually happened? There should be no SEGFAULT Anything else you want to add: I have a guess on what the problem might be; that `find_paths_multitree` is not properly initializing path.buf. I determined this because, in my limited testing, if I always use `find_paths_generic` we see that all the pointers are NULL and we don't SEGFAULT. Please review the rest of the bug report below. You can delete any lines you don't wish to share. [System Info] git version: git version 2.48.0.rc1.242.gedf34e3ab4 cpu: x86_64 no commit associated with this build sizeof-long: 8 sizeof-size_t: 8 shell-path: /bin/sh libcurl: 8.11.0 OpenSSL: OpenSSL 3.4.0 22 Oct 2024 zlib: 1.3.1 uname: Linux 6.12.6-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 19 Dec 2024 21:29:01 +0000 x86_64 compiler info: gnuc: 14.2 libc info: glibc: 2.40 $SHELL (typically, interactive shell): /bin/bash [Enabled Hooks]