* [PATCH v1] path-walk: fix NULL pointer dereference in error message
@ 2026-03-20 11:48 Yuvraj Singh Chauhan
2026-03-20 15:16 ` D. Ben Knoble
0 siblings, 1 reply; 7+ messages in thread
From: Yuvraj Singh Chauhan @ 2026-03-20 11:48 UTC (permalink / raw)
To: git; +Cc: christian.couder, stolee, Yuvraj Singh Chauhan
When lookup_tree() or lookup_blob() cannot find a tree entry's object,
'o' is set to NULL via:
o = child ? &child->object : NULL;
The subsequent null-check catches this correctly, but then dereferences
'o' to format the error message:
error(_("failed to find object %s"), oid_to_hex(&o->oid));
This causes a segfault instead of the intended diagnostic output.
Fix this by using &entry.oid instead. 'entry' is the struct name_entry
populated by tree_entry() on each loop iteration and holds the OID of
the failing lookup -- which is exactly what the error should report.
This crash is reachable via git-backfill(1) when a tree entry's object
is absent from the local object database.
Signed-off-by: Yuvraj Singh Chauhan <ysinghcin@gmail.com>
---
path-walk.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/path-walk.c b/path-walk.c
index 364e4cfa19..839582380c 100644
--- a/path-walk.c
+++ b/path-walk.c
@@ -171,7 +171,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
if (!o) {
error(_("failed to find object %s"),
- oid_to_hex(&o->oid));
+ oid_to_hex(&entry.oid));
return -1;
}
--
2.53.0.582.gca1db8a0f7
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v1] path-walk: fix NULL pointer dereference in error message
2026-03-20 11:48 [PATCH v1] path-walk: fix NULL pointer dereference in error message Yuvraj Singh Chauhan
@ 2026-03-20 15:16 ` D. Ben Knoble
2026-03-20 16:14 ` Derrick Stolee
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: D. Ben Knoble @ 2026-03-20 15:16 UTC (permalink / raw)
To: Yuvraj Singh Chauhan; +Cc: git, christian.couder, stolee
On Fri, Mar 20, 2026 at 7:50 AM Yuvraj Singh Chauhan
<ysinghcin@gmail.com> wrote:
>
> When lookup_tree() or lookup_blob() cannot find a tree entry's object,
> 'o' is set to NULL via:
>
> o = child ? &child->object : NULL;
>
> The subsequent null-check catches this correctly, but then dereferences
> 'o' to format the error message:
>
> error(_("failed to find object %s"), oid_to_hex(&o->oid));
>
> This causes a segfault instead of the intended diagnostic output.
>
> Fix this by using &entry.oid instead. 'entry' is the struct name_entry
> populated by tree_entry() on each loop iteration and holds the OID of
> the failing lookup -- which is exactly what the error should report.
>
> This crash is reachable via git-backfill(1) when a tree entry's object
> is absent from the local object database.
>
> Signed-off-by: Yuvraj Singh Chauhan <ysinghcin@gmail.com>
> ---
> path-walk.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/path-walk.c b/path-walk.c
> index 364e4cfa19..839582380c 100644
> --- a/path-walk.c
> +++ b/path-walk.c
> @@ -171,7 +171,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
>
> if (!o) {
> error(_("failed to find object %s"),
> - oid_to_hex(&o->oid));
> + oid_to_hex(&entry.oid));
> return -1;
> }
>
> --
> 2.53.0.582.gca1db8a0f7
Interesting find. I was hoping to see an easy way to reproduce hitting
this code, and after grepping around a bit I found a few places that
end up in this code (git-backfill and git-repo being the primary
callers of walk_objects_by_path), but on second glance I think "!o" is
current dead code.
When we compute "child" in either preceding branch using lookup_tree
or lookup_blob, we only return NULL if !quiet in the object_as_type
calls (assuming we hit the "else" case there, anyway). But quiet==0 in
both callers along this path, so !quiet will be truthy and we'll
error() out there instead, never returning to add_tree_entries.
Since I didn't quickly come up with a reproduction, I can't quite
prove this, anyway. It's also possible my analysis is based on code
that has since changed (I happened to have a537e3e6e9 (Merge branch
'sp/send-email-validate-charset' into next, 2026-03-06) checked out at
the moment).
Still, fixing such obviously wrong dereference is good, but I wonder
if we should go further?
You mentioned git-backfill with a tree missing from the local odb; do
you have a short reproduction script or test-case?
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1] path-walk: fix NULL pointer dereference in error message
2026-03-20 15:16 ` D. Ben Knoble
@ 2026-03-20 16:14 ` Derrick Stolee
2026-03-23 9:45 ` Yuvraj Singh Chauhan
2026-03-20 17:08 ` Junio C Hamano
2026-03-20 18:54 ` René Scharfe
2 siblings, 1 reply; 7+ messages in thread
From: Derrick Stolee @ 2026-03-20 16:14 UTC (permalink / raw)
To: D. Ben Knoble, Yuvraj Singh Chauhan; +Cc: git, christian.couder
On 3/20/2026 11:16 AM, D. Ben Knoble wrote:
> On Fri, Mar 20, 2026 at 7:50 AM Yuvraj Singh Chauhan
> <ysinghcin@gmail.com> wrote:
>> @@ -171,7 +171,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
>>
>> if (!o) {
>> error(_("failed to find object %s"),
>> - oid_to_hex(&o->oid));
>> + oid_to_hex(&entry.oid));
>> return -1;
>> }
>>
>> --
>> 2.53.0.582.gca1db8a0f7
>
> Interesting find. I was hoping to see an easy way to reproduce hitting
> this code, and after grepping around a bit I found a few places that
> end up in this code (git-backfill and git-repo being the primary
> callers of walk_objects_by_path), but on second glance I think "!o" is
> current dead code.
I can appreciate that the existing code is clearly incorrect, so
tooling scanning code for defects would find this even if we can't
easily create a test case to demonstrate it.
> Still, fixing such obviously wrong dereference is good, but I wonder
> if we should go further?
>
> You mentioned git-backfill with a tree missing from the local odb; do
> you have a short reproduction script or test-case?
I imagine that it would be difficult to set up such a case, but maybe
it would follow these steps (based on existing 'git backfill' tests
that start with a partial clone):
1. Make a bare, blobless partial clone of the server repo.
2. Explode the client repo's object store into loose objects.
3. Delete a loose tree object, but one that isn't a commit's root
tree. It must be a child tree.
In this case, we should hit this issue. Blobless partial clones
expect all reachable trees to exist locally and so are not prepared
to download missing trees on-demand.
Thanks,
-Stolee
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1] path-walk: fix NULL pointer dereference in error message
2026-03-20 15:16 ` D. Ben Knoble
2026-03-20 16:14 ` Derrick Stolee
@ 2026-03-20 17:08 ` Junio C Hamano
2026-03-20 18:54 ` René Scharfe
2 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2026-03-20 17:08 UTC (permalink / raw)
To: D. Ben Knoble; +Cc: Yuvraj Singh Chauhan, git, christian.couder, stolee
"D. Ben Knoble" <ben.knoble@gmail.com> writes:
> You mentioned git-backfill with a tree missing from the local odb; do
> you have a short reproduction script or test-case?
Interesting thing to ask. THe code looks correct, though.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1] path-walk: fix NULL pointer dereference in error message
2026-03-20 15:16 ` D. Ben Knoble
2026-03-20 16:14 ` Derrick Stolee
2026-03-20 17:08 ` Junio C Hamano
@ 2026-03-20 18:54 ` René Scharfe
2026-03-22 14:21 ` D. Ben Knoble
2 siblings, 1 reply; 7+ messages in thread
From: René Scharfe @ 2026-03-20 18:54 UTC (permalink / raw)
To: D. Ben Knoble, Yuvraj Singh Chauhan; +Cc: git, christian.couder, stolee
On 3/20/26 4:16 PM, D. Ben Knoble wrote:
>
> When we compute "child" in either preceding branch using lookup_tree
> or lookup_blob, we only return NULL if !quiet in the object_as_type
> calls (assuming we hit the "else" case there, anyway). But quiet==0 in
> both callers along this path, so !quiet will be truthy and we'll
> error() out there instead, never returning to add_tree_entries.
error() just prints a message, it doesn't end the program.
> Since I didn't quickly come up with a reproduction, I can't quite
> prove this, anyway. It's also possible my analysis is based on code
> that has since changed (I happened to have a537e3e6e9 (Merge branch
> 'sp/send-email-validate-charset' into next, 2026-03-06) checked out at
> the moment).
We could build a tree referencing an object using a mismatched
type to hit that. It's possible by removing the type check from
builtin/mktree.c:mktree_line(), then using the resulting twisted tool:
$ commit=$(git rev-parse HEAD)
$ tree=$(printf "100644 blob $commit\tcommit\n" | git_evil mktree)
> Still, fixing such obviously wrong dereference is good, but I wonder
> if we should go further?
>
> You mentioned git-backfill with a tree missing from the local odb; do
> you have a short reproduction script or test-case?
I don't know about backfill, but this would work:
$ echo $tree | git pack-objects --path-walk --all foo
René
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1] path-walk: fix NULL pointer dereference in error message
2026-03-20 18:54 ` René Scharfe
@ 2026-03-22 14:21 ` D. Ben Knoble
0 siblings, 0 replies; 7+ messages in thread
From: D. Ben Knoble @ 2026-03-22 14:21 UTC (permalink / raw)
To: René Scharfe; +Cc: Yuvraj Singh Chauhan, git, christian.couder, stolee
On Fri, Mar 20, 2026 at 2:54 PM René Scharfe <l.s.r@web.de> wrote:
>
> On 3/20/26 4:16 PM, D. Ben Knoble wrote:
> >
> > When we compute "child" in either preceding branch using lookup_tree
> > or lookup_blob, we only return NULL if !quiet in the object_as_type
> > calls (assuming we hit the "else" case there, anyway). But quiet==0 in
> > both callers along this path, so !quiet will be truthy and we'll
> > error() out there instead, never returning to add_tree_entries.
>
> error() just prints a message, it doesn't end the program.
Ah, thanks! The rest is probably moot then.
>
> > Since I didn't quickly come up with a reproduction, I can't quite
> > prove this, anyway. It's also possible my analysis is based on code
> > that has since changed (I happened to have a537e3e6e9 (Merge branch
> > 'sp/send-email-validate-charset' into next, 2026-03-06) checked out at
> > the moment).
>
> We could build a tree referencing an object using a mismatched
> type to hit that. It's possible by removing the type check from
> builtin/mktree.c:mktree_line(), then using the resulting twisted tool:
>
> $ commit=$(git rev-parse HEAD)
> $ tree=$(printf "100644 blob $commit\tcommit\n" | git_evil mktree)
>
> > Still, fixing such obviously wrong dereference is good, but I wonder
> > if we should go further?
> >
> > You mentioned git-backfill with a tree missing from the local odb; do
> > you have a short reproduction script or test-case?
>
> I don't know about backfill, but this would work:
>
> $ echo $tree | git pack-objects --path-walk --all foo
>
> René
>
Thanks all.
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1] path-walk: fix NULL pointer dereference in error message
2026-03-20 16:14 ` Derrick Stolee
@ 2026-03-23 9:45 ` Yuvraj Singh Chauhan
0 siblings, 0 replies; 7+ messages in thread
From: Yuvraj Singh Chauhan @ 2026-03-23 9:45 UTC (permalink / raw)
To: Derrick Stolee; +Cc: D. Ben Knoble, git, christian.couder
On Fri, Mar 20, 2026 at 12:14:19PM -0400, Derrick Stolee wrote:
> On 3/20/2026 11:16 AM, D. Ben Knoble wrote:
> > On Fri, Mar 20, 2026 at 7:50 AM Yuvraj Singh Chauhan
> > <ysinghcin@gmail.com> wrote:
> >> @@ -171,7 +171,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
> >>
> >> if (!o) {
> >> error(_("failed to find object %s"),
> >> - oid_to_hex(&o->oid));
> >> + oid_to_hex(&entry.oid));
> >> return -1;
> >> }
> >>
> >> --
> >> 2.53.0.582.gca1db8a0f7
> >
> > Interesting find. I was hoping to see an easy way to reproduce hitting
> > this code, and after grepping around a bit I found a few places that
> > end up in this code (git-backfill and git-repo being the primary
> > callers of walk_objects_by_path), but on second glance I think "!o" is
> > current dead code.
>
> I can appreciate that the existing code is clearly incorrect, so
> tooling scanning code for defects would find this even if we can't
> easily create a test case to demonstrate it.
>
> > Still, fixing such obviously wrong dereference is good, but I wonder
> > if we should go further?
> >
> > You mentioned git-backfill with a tree missing from the local odb; do
> > you have a short reproduction script or test-case?
>
> I imagine that it would be difficult to set up such a case, but maybe
> it would follow these steps (based on existing 'git backfill' tests
> that start with a partial clone):
>
> 1. Make a bare, blobless partial clone of the server repo.
>
> 2. Explode the client repo's object store into loose objects.
>
> 3. Delete a loose tree object, but one that isn't a commit's root
> tree. It must be a child tree.
>
> In this case, we should hit this issue. Blobless partial clones
> expect all reachable trees to exist locally and so are not prepared
> to download missing trees on-demand.
>
> Thanks,
> -Stolee
Thanks for the suggestions on how to reproduce this.
I traced through the code paths and believe the scenario described
(blobless partial clone, explode objects, delete a child tree) would
not actually reach the '!o' branch. Here is my reasoning:
When add_tree_entries() encounters a child tree entry whose OID is
missing from the ODB, it calls lookup_tree(). Since the OID was never
previously registered in the parsed object hash, lookup_object()
returns NULL, and lookup_tree() falls through to create_object() which
allocates a shell object. So 'child' is non-NULL, 'o' is non-NULL,
and the '!o' check is not reached.
The missing object would instead fail later: when the path-walk tries
to visit the child tree via a subsequent add_tree_entries() call,
repo_parse_tree_gently() attempts to read it from the ODB, fails, and
returns the "bad tree object" error at the top of the function.
The '!o' path is reachable when the OID in a tree entry was
"previously registered" in the parsed object hash with a different
type. In that case, lookup_blob() or lookup_tree() calls
object_as_type(), which detects the type conflict and returns NULL.
This is what happens with a corrupt/confused tree that references a
commit OID as a blob entry, the commit was already registered during
the revision walk, so object_as_type(obj, OBJ_BLOB, 0) returns NULL.
For v2, I've added a test that exercises exactly this path:
# Create a tree with a blob entry pointing to a commit object
commit=$(git rev-parse HEAD)
bad_tree=$(perl -e 'print "100644 confused\0" . pack("H*", $ARGV[0])' \
"$commit" | git hash-object -w --stdin -t tree)
git pack-objects --path-walk --all /tmp/test-pack <<< "$bad_tree"
This constructs a tree where mode 100644 (blob) has a commit OID.
During --path-walk, the commit is registered as OBJ_COMMIT by the
revision walk via --all, so when the tree entry is processed,
lookup_blob() → object_as_type() returns NULL, triggering the bug.
Please review
Sincerely,
Yuvraj
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-03-23 9:45 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-20 11:48 [PATCH v1] path-walk: fix NULL pointer dereference in error message Yuvraj Singh Chauhan
2026-03-20 15:16 ` D. Ben Knoble
2026-03-20 16:14 ` Derrick Stolee
2026-03-23 9:45 ` Yuvraj Singh Chauhan
2026-03-20 17:08 ` Junio C Hamano
2026-03-20 18:54 ` René Scharfe
2026-03-22 14:21 ` D. Ben Knoble
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox