git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] pack-bitmap: remove checks before bitmap_free
@ 2025-05-25  5:09 Lidong Yan via GitGitGadget
  2025-05-26  6:49 ` Patrick Steinhardt
  2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
  0 siblings, 2 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-05-25  5:09 UTC (permalink / raw)
  To: git; +Cc: Lidong Yan, Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects, we build a roots_bitmap and
cascade it to cb.base. However, I’m wondering why we only free
roots_bitmap when the cascade succeeds. It seems we could safely remove
this check and always free roots_bitmap afterward, which might provide
some performance benefits.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
    pack-bitmap: remove checks before bitmap_free
    
    In pack-bitmap.c:find_boundary_objects, remove cascade success check and
    always free roots_bitmap afterward to make static analysis tool works
    better.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v1
Pull-Request: https://github.com/git/git/pull/1977

 pack-bitmap.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c..8727f316de9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*

base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH] pack-bitmap: remove checks before bitmap_free
  2025-05-25  5:09 [PATCH] pack-bitmap: remove checks before bitmap_free Lidong Yan via GitGitGadget
@ 2025-05-26  6:49 ` Patrick Steinhardt
  2025-05-26 16:05   ` lidongyan
  2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
  1 sibling, 1 reply; 28+ messages in thread
From: Patrick Steinhardt @ 2025-05-26  6:49 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget; +Cc: git, Lidong Yan

On Sun, May 25, 2025 at 05:09:43AM +0000, Lidong Yan via GitGitGadget wrote:
> From: Lidong Yan <502024330056@smail.nju.edu.cn>
> 
> In pack-bitmap.c:find_boundary_objects, we build a roots_bitmap and
> cascade it to cb.base. However, I’m wondering why we only free
> roots_bitmap when the cascade succeeds. It seems we could safely remove
> this check and always free roots_bitmap afterward, which might provide
> some performance benefits.

This commit message isn't quite a convincing one. As author of a patch
the onus falls on you to explain why the change is sensible, but even
more importantly it also falls on you to explain why it is correct.

It is of course fine to ask for help and input, but in that case you
should probably mark the patch accordingly, for example with the RFC
tag.

> diff --git a/pack-bitmap.c b/pack-bitmap.c
> index ac6d62b980c..8727f316de9 100644
> --- a/pack-bitmap.c
> +++ b/pack-bitmap.c
> @@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
>  			bitmap_set(roots_bitmap, pos);
>  		}
>  
> -		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
> -			bitmap_free(roots_bitmap);
> +		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
> +		bitmap_free(roots_bitmap);
>  	}

We know that `roots_bitmap` is always allocated via `bitmap_new()`, so
it won't ever be a `NULL` pointer and should in theory always be free'd.
Furthermore, we know that the pointer never escapes the local scope,
either.

The next question would thus be: what does `cascade_pseudo_merges_1()`
do with the bitmap? Are there situations where it does free it for us,
or where it moves ownership of that bitmap? So let's go down the call
chain:

  - `cascade_pseudo_merges_1()` passes it on to
    `cascade_pseudo_merges()`.

  - `cascade_pseudo_merges()` passes it on to `apply_pseudo_merge()`.

`apply_pseudo_merge()` itself then checks whether the pseudo-merge is a
subset of the `roots_bitmap` and, if not, ORs the pseudo-merge into it.

None of these operations move around ownership or free the bitmap, so
this looks like a true memory leak in case `cascade_pseudo_merges_1()`
returns non-zero. Which would raise another question: when exactly does
it return non-zero, and can we trigger the memory leak via a test?

Information like this should ideally be part of the commit message
itself. It helps reviewers to figure out _why_ a change is correct and,
if anybody were to dig into history, would also help them to have enough
context.

Thanks!

Patrick

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH] pack-bitmap: remove checks before bitmap_free
  2025-05-26  6:49 ` Patrick Steinhardt
@ 2025-05-26 16:05   ` lidongyan
  0 siblings, 0 replies; 28+ messages in thread
From: lidongyan @ 2025-05-26 16:05 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: Lidong Yan via GitGitGadget, git

2025年5月26日 14:49,Patrick Steinhardt <ps@pks.im> 写道:
> 
> This commit message isn't quite a convincing one. As author of a patch
> the onus falls on you to explain why the change is sensible, but even
> more importantly it also falls on you to explain why it is correct.
> 
> It is of course fine to ask for help and input, but in that case you
> should probably mark the patch accordingly, for example with the RFC
> tag.

Thank you for the suggestion. Please allow me to keep the patch in its
 current form this time. 

> We know that `roots_bitmap` is always allocated via `bitmap_new()`, so
> it won't ever be a `NULL` pointer and should in theory always be free'd.
> Furthermore, we know that the pointer never escapes the local scope,
> either.
> 
> The next question would thus be: what does `cascade_pseudo_merges_1()`
> do with the bitmap? Are there situations where it does free it for us,
> or where it moves ownership of that bitmap? So let's go down the call
> chain:
> 
>  - `cascade_pseudo_merges_1()` passes it on to
>    `cascade_pseudo_merges()`.
> 
>  - `cascade_pseudo_merges()` passes it on to `apply_pseudo_merge()`.
> 
> `apply_pseudo_merge()` itself then checks whether the pseudo-merge is a
> subset of the `roots_bitmap` and, if not, ORs the pseudo-merge into it.

I would put this into commit message. I also noticed that `find_objects()` in
pack-bitmap.c has similar code but without `if (cascade_pseudo_merges_1)`.

> 
> None of these operations move around ownership or free the bitmap, so
> this looks like a true memory leak in case `cascade_pseudo_merges_1()`
> returns non-zero. Which would raise another question: when exactly does
> it return non-zero, and can we trigger the memory leak via a test?

Seems we need to make `bitmap_git->pseudo_merges->v[I]` contains some
objects which doesn’t exist in `roots`. I’ll try to figure it out in the next patch. 

Thanks
Lidong


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v2 0/2] pack-bitmap: remove checks before bitmap_free
  2025-05-25  5:09 [PATCH] pack-bitmap: remove checks before bitmap_free Lidong Yan via GitGitGadget
  2025-05-26  6:49 ` Patrick Steinhardt
@ 2025-05-30 18:14 ` Lidong Yan via GitGitGadget
  2025-05-30 18:14   ` [PATCH v2 1/2] " Lidong Yan via GitGitGadget
                     ` (3 more replies)
  1 sibling, 4 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-05-30 18:14 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Lidong Yan

In pack-bitmap.c:find_boundary_objects, remove cascade success check and
always free roots_bitmap afterward to make static analysis tool works
better.

Lidong Yan (2):
  pack-bitmap: remove checks before bitmap_free
  t5333: test memory leak when use pseudo-merge in boundary traversal

 pack-bitmap.c                   |  4 ++--
 t/t5333-pseudo-merge-bitmaps.sh | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 2 deletions(-)


base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v2
Pull-Request: https://github.com/git/git/pull/1977

Range-diff vs v1:

 1:  19677bcbc3d ! 1:  d7b7a0e29ec pack-bitmap: remove checks before bitmap_free
     @@ Commit message
          pack-bitmap: remove checks before bitmap_free
      
          In pack-bitmap.c:find_boundary_objects, we build a roots_bitmap and
     -    cascade it to cb.base. However, I’m wondering why we only free
     -    roots_bitmap when the cascade succeeds. It seems we could safely remove
     -    this check and always free roots_bitmap afterward, which might provide
     -    some performance benefits.
     +    cascade it to cb.base. Only when cascade failed, roots_bitmap is
     +    freed otherwise it leaks. Since cascade_pseudo_merges_1() only use
     +    roots_bitmap as a mutable reference not takes roots_bitmap's ownership
     +    we'd better remove `if(cascade_pseudo_merges_1)` and frees roots_bitmap
     +    anyway.
      
          Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
      
 -:  ----------- > 2:  56b24d681cb t5333: test memory leak when use pseudo-merge in boundary traversal

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v2 1/2] pack-bitmap: remove checks before bitmap_free
  2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
@ 2025-05-30 18:14   ` Lidong Yan via GitGitGadget
  2025-05-30 18:14   ` [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal Lidong Yan via GitGitGadget
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-05-30 18:14 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Lidong Yan, Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects, we build a roots_bitmap and
cascade it to cb.base. Only when cascade failed, roots_bitmap is
freed otherwise it leaks. Since cascade_pseudo_merges_1() only use
roots_bitmap as a mutable reference not takes roots_bitmap's ownership
we'd better remove `if(cascade_pseudo_merges_1)` and frees roots_bitmap
anyway.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
 pack-bitmap.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c5..8727f316de92 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal
  2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
  2025-05-30 18:14   ` [PATCH v2 1/2] " Lidong Yan via GitGitGadget
@ 2025-05-30 18:14   ` Lidong Yan via GitGitGadget
  2025-05-30 21:42     ` Junio C Hamano
  2025-05-30 21:06   ` [PATCH v2 0/2] pack-bitmap: remove checks before bitmap_free Junio C Hamano
  2025-06-03  1:46   ` [PATCH v3] " Lidong Yan via GitGitGadget
  3 siblings, 1 reply; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-05-30 18:14 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Lidong Yan, Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
if cascade_pseudo_merges_1() fails. Otherwise, it leaks, leading to
a memory leak that currently lacks a dedicated test to detect it.

To trigger this leak, we need a pseudo-merge whose size is equal to
or smaller than roots_bitmap (which corresponds to the set of "haves"
commits in prepare_bitmap_walk()). To do this, we can create two
commits: A and B. Add A to the pseudo-merge list and perform a traversal
over the range A..B. In this scenario, the "haves" set will be {A},
and cascade_pseudo_merges_1() will succeed — thereby exposing the leak
due to the missing roots_bitmap cleanup.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
 t/t5333-pseudo-merge-bitmaps.sh | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 56674db562f9..5e263fce50a7 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -445,4 +445,24 @@ test_expect_success 'pseudo-merge closure' '
 	)
 '
 
+test_expect_success 'use pseudo-merge in boundary traversal' '
+	git init pseudo-merge-boundary-traversal &&
+	(
+		cd pseudo-merge-boundary-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/ &&
+		git config bitmapPseudoMerge.test.threshold now &&
+		git config bitmapPseudoMerge.test.stableThreshold now &&
+		export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
+
+		test_commit A &&
+		git repack -adb &&
+		test_commit B &&
+
+		echo '1' >expect &&
+		git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v2 0/2] pack-bitmap: remove checks before bitmap_free
  2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
  2025-05-30 18:14   ` [PATCH v2 1/2] " Lidong Yan via GitGitGadget
  2025-05-30 18:14   ` [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal Lidong Yan via GitGitGadget
@ 2025-05-30 21:06   ` Junio C Hamano
  2025-06-03  1:46   ` [PATCH v3] " Lidong Yan via GitGitGadget
  3 siblings, 0 replies; 28+ messages in thread
From: Junio C Hamano @ 2025-05-30 21:06 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget; +Cc: git, Patrick Steinhardt, Lidong Yan

"Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:

> In pack-bitmap.c:find_boundary_objects, remove cascade success check and
> always free roots_bitmap afterward to make static analysis tool works
> better.
>
> Lidong Yan (2):
>   pack-bitmap: remove checks before bitmap_free
>   t5333: test memory leak when use pseudo-merge in boundary traversal

How would these two commits relate to each other?  If [2/2] is a
test that exposes existing breakage if [1/2] weren't there, we
usually have them in the same commit.  If they are not related, of
course, they can be applied and advanced independently.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal
  2025-05-30 18:14   ` [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal Lidong Yan via GitGitGadget
@ 2025-05-30 21:42     ` Junio C Hamano
  2025-05-30 21:50       ` Eric Sunshine
  0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2025-05-30 21:42 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget; +Cc: git, Patrick Steinhardt, Lidong Yan

"Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +		export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&

The test linter complains on this line for me, it seems.

I've ran out of time for today's integration cycle, so this topic
will not be in what I'll push out later this afternoon.

Thanks.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal
  2025-05-30 21:42     ` Junio C Hamano
@ 2025-05-30 21:50       ` Eric Sunshine
  2025-05-31  3:18         ` lidongyan
  0 siblings, 1 reply; 28+ messages in thread
From: Eric Sunshine @ 2025-05-30 21:50 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt, Lidong Yan

On Fri, May 30, 2025 at 5:42 PM Junio C Hamano <gitster@pobox.com> wrote:
> "Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:
> > +             export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
>
> The test linter complains on this line for me, it seems.

To provide a bit more context:

    % (cd t && make test-lint-shell-syntax)

tells you that `export FOO=bar` is not portable and that it should
instead be written as:

    FOO=bar &&
    export FOO &&

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal
  2025-05-30 21:50       ` Eric Sunshine
@ 2025-05-31  3:18         ` lidongyan
  0 siblings, 0 replies; 28+ messages in thread
From: lidongyan @ 2025-05-31  3:18 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Junio C Hamano, Lidong Yan via GitGitGadget, git,
	Patrick Steinhardt

2025年5月31日 05:50,Eric Sunshine <sunshine@sunshineco.com> 写道:
> 
> On Fri, May 30, 2025 at 5:42 PM Junio C Hamano <gitster@pobox.com> wrote:
>> "Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:
>>> +             export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
>> 
>> The test linter complains on this line for me, it seems.
> 
> To provide a bit more context:
> 
>    % (cd t && make test-lint-shell-syntax)

Thanks, I should run this before submit.

> 
> tells you that `export FOO=bar` is not portable and that it should
> instead be written as:
> 
>    FOO=bar &&
>    export FOO &&
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
                     ` (2 preceding siblings ...)
  2025-05-30 21:06   ` [PATCH v2 0/2] pack-bitmap: remove checks before bitmap_free Junio C Hamano
@ 2025-06-03  1:46   ` Lidong Yan via GitGitGadget
  2025-06-03  6:12     ` Junio C Hamano
  2025-06-03  6:20     ` [PATCH v4] " Lidong Yan via GitGitGadget
  3 siblings, 2 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-06-03  1:46 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Eric Sunshine, Lidong Yan, Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
use roots_bitmap as a mutable reference but not takes roots_bitmap's
ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
And this leak currently lacks a dedicated test to detect it.

To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
always calling bitmap_free(roots_bitmap);

To trigger this leak, we need a pseudo-merge whose size is equal to
or smaller than roots_bitmap (which corresponds to the set of "haves"
commits in prepare_bitmap_walk()). To do this, we can create two
commits: A and B. Add A to the pseudo-merge list and perform a traversal
over the range A..B. In this scenario, the "haves" set will be {A},
and cascade_pseudo_merges_1() will succeed, thereby exposing the leak
due to the missing roots_bitmap cleanup.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
    pack-bitmap: remove checks before bitmap_free
    
    In pack-bitmap.c:find_boundary_objects, remove cascade success check and
    always free roots_bitmap afterward to make static analysis tool works
    better.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v3
Pull-Request: https://github.com/git/git/pull/1977

Range-diff vs v2:

 1:  d7b7a0e29ec < -:  ----------- pack-bitmap: remove checks before bitmap_free
 2:  56b24d681cb ! 1:  151a7f5dc70 t5333: test memory leak when use pseudo-merge in boundary traversal
     @@ Metadata
      Author: Lidong Yan <502024330056@smail.nju.edu.cn>
      
       ## Commit message ##
     -    t5333: test memory leak when use pseudo-merge in boundary traversal
     +    pack-bitmap: remove checks before bitmap_free
      
          In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
     -    if cascade_pseudo_merges_1() fails. Otherwise, it leaks, leading to
     -    a memory leak that currently lacks a dedicated test to detect it.
     +    if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
     +    use roots_bitmap as a mutable reference but not takes roots_bitmap's
     +    ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
     +    And this leak currently lacks a dedicated test to detect it.
     +
     +    To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
     +    always calling bitmap_free(roots_bitmap);
      
          To trigger this leak, we need a pseudo-merge whose size is equal to
          or smaller than roots_bitmap (which corresponds to the set of "haves"
          commits in prepare_bitmap_walk()). To do this, we can create two
          commits: A and B. Add A to the pseudo-merge list and perform a traversal
          over the range A..B. In this scenario, the "haves" set will be {A},
     -    and cascade_pseudo_merges_1() will succeed — thereby exposing the leak
     +    and cascade_pseudo_merges_1() will succeed, thereby exposing the leak
          due to the missing roots_bitmap cleanup.
      
          Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
      
     + ## pack-bitmap.c ##
     +@@ pack-bitmap.c: static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
     + 			bitmap_set(roots_bitmap, pos);
     + 		}
     + 
     +-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
     +-			bitmap_free(roots_bitmap);
     ++		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
     ++		bitmap_free(roots_bitmap);
     + 	}
     + 
     + 	/*
     +
       ## t/t5333-pseudo-merge-bitmaps.sh ##
      @@ t/t5333-pseudo-merge-bitmaps.sh: test_expect_success 'pseudo-merge closure' '
       	)
     @@ t/t5333-pseudo-merge-bitmaps.sh: test_expect_success 'pseudo-merge closure' '
      +		git config bitmapPseudoMerge.test.pattern refs/ &&
      +		git config bitmapPseudoMerge.test.threshold now &&
      +		git config bitmapPseudoMerge.test.stableThreshold now &&
     -+		export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
     ++		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
      +
      +		test_commit A &&
      +		git repack -adb &&


 pack-bitmap.c                   |  4 ++--
 t/t5333-pseudo-merge-bitmaps.sh | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c..8727f316de9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 56674db562f..454f8c7a817 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -445,4 +445,24 @@ test_expect_success 'pseudo-merge closure' '
 	)
 '
 
+test_expect_success 'use pseudo-merge in boundary traversal' '
+	git init pseudo-merge-boundary-traversal &&
+	(
+		cd pseudo-merge-boundary-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/ &&
+		git config bitmapPseudoMerge.test.threshold now &&
+		git config bitmapPseudoMerge.test.stableThreshold now &&
+		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
+
+		test_commit A &&
+		git repack -adb &&
+		test_commit B &&
+
+		echo '1' >expect &&
+		git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done

base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-03  1:46   ` [PATCH v3] " Lidong Yan via GitGitGadget
@ 2025-06-03  6:12     ` Junio C Hamano
  2025-06-03  6:22       ` lidongyan
  2025-06-03  6:20     ` [PATCH v4] " Lidong Yan via GitGitGadget
  1 sibling, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2025-06-03  6:12 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget
  Cc: git, Patrick Steinhardt, Eric Sunshine, Lidong Yan

"Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +test_expect_success 'use pseudo-merge in boundary traversal' '
> +	git init pseudo-merge-boundary-traversal &&
> +	(
> +		cd pseudo-merge-boundary-traversal &&
> +
> +		git config bitmapPseudoMerge.test.pattern refs/ &&
> +		git config bitmapPseudoMerge.test.threshold now &&
> +		git config bitmapPseudoMerge.test.stableThreshold now &&


> +		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&

Either before or after that line, don't you need to 

		export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL &&

as well?

And if the test passed without exporting the variable, is it really
testing what we want to test?

> +		test_commit A &&
> +		git repack -adb &&
> +		test_commit B &&
> +
> +		echo '1' >expect &&
> +		git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
> +		test_cmp expect actual
> +	)
> +'
> +
>  test_done
>
> base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v4] pack-bitmap: remove checks before bitmap_free
  2025-06-03  1:46   ` [PATCH v3] " Lidong Yan via GitGitGadget
  2025-06-03  6:12     ` Junio C Hamano
@ 2025-06-03  6:20     ` Lidong Yan via GitGitGadget
  2025-06-03 22:09       ` Taylor Blau
  2025-06-05  6:24       ` [PATCH v5] " Lidong Yan via GitGitGadget
  1 sibling, 2 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-06-03  6:20 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Eric Sunshine, Lidong Yan, Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
use roots_bitmap as a mutable reference but not takes roots_bitmap's
ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
And this leak currently lacks a dedicated test to detect it.

To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
always calling bitmap_free(roots_bitmap);

To trigger this leak, we need a pseudo-merge whose size is equal to
or smaller than roots_bitmap (which corresponds to the set of "haves"
commits in prepare_bitmap_walk()). To do this, we can create two
commits: A and B. Add A to the pseudo-merge list and perform a traversal
over the range A..B. In this scenario, the "haves" set will be {A},
and cascade_pseudo_merges_1() will succeed, thereby exposing the leak
due to the missing roots_bitmap cleanup.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
    pack-bitmap: remove checks before bitmap_free
    
    In pack-bitmap.c:find_boundary_objects, remove cascade success check and
    always free roots_bitmap afterward to make static analysis tool works
    better.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v4
Pull-Request: https://github.com/git/git/pull/1977

Range-diff vs v3:

 1:  151a7f5dc70 ! 1:  fa443065436 pack-bitmap: remove checks before bitmap_free
     @@ t/t5333-pseudo-merge-bitmaps.sh: test_expect_success 'pseudo-merge closure' '
      +		git config bitmapPseudoMerge.test.pattern refs/ &&
      +		git config bitmapPseudoMerge.test.threshold now &&
      +		git config bitmapPseudoMerge.test.stableThreshold now &&
     -+		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
      +
      +		test_commit A &&
      +		git repack -adb &&
      +		test_commit B &&
      +
      +		echo '1' >expect &&
     -+		git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
     ++		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
     ++			git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
      +		test_cmp expect actual
      +	)
      +'


 pack-bitmap.c                   |  4 ++--
 t/t5333-pseudo-merge-bitmaps.sh | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c..8727f316de9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 56674db562f..e665001a410 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -445,4 +445,24 @@ test_expect_success 'pseudo-merge closure' '
 	)
 '
 
+test_expect_success 'use pseudo-merge in boundary traversal' '
+	git init pseudo-merge-boundary-traversal &&
+	(
+		cd pseudo-merge-boundary-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/ &&
+		git config bitmapPseudoMerge.test.threshold now &&
+		git config bitmapPseudoMerge.test.stableThreshold now &&
+
+		test_commit A &&
+		git repack -adb &&
+		test_commit B &&
+
+		echo '1' >expect &&
+		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
+			git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done

base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-03  6:12     ` Junio C Hamano
@ 2025-06-03  6:22       ` lidongyan
  2025-06-03 15:14         ` Junio C Hamano
  0 siblings, 1 reply; 28+ messages in thread
From: lidongyan @ 2025-06-03  6:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine

2025年6月3日 14:12,Junio C Hamano <gitster@pobox.com> 写道:
> 
> "Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>> +test_expect_success 'use pseudo-merge in boundary traversal' '
>> + git init pseudo-merge-boundary-traversal &&
>> + (
>> + cd pseudo-merge-boundary-traversal &&
>> +
>> + git config bitmapPseudoMerge.test.pattern refs/ &&
>> + git config bitmapPseudoMerge.test.threshold now &&
>> + git config bitmapPseudoMerge.test.stableThreshold now &&
> 
> 
>> + GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 &&
> 
> Either before or after that line, don't you need to 
> 
> export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL &&
> 
> as well?
> 
> And if the test passed without exporting the variable, is it really
> testing what we want to test?
> 

Sorry about that. I should put GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
In front of `git rev-list …` so that when traverse bitmap it enters `pack-bitmap:find_boundary_objects()`.

>> + test_commit A &&
>> + git repack -adb &&
>> + test_commit B &&
>> +
>> + echo '1' >expect &&
>> + git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
>> + test_cmp expect actual
>> + )
>> +'
>> +
>> test_done
>> 
>> base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-03  6:22       ` lidongyan
@ 2025-06-03 15:14         ` Junio C Hamano
  2025-06-03 15:32           ` lidongyan
  0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2025-06-03 15:14 UTC (permalink / raw)
  To: lidongyan
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine

lidongyan <502024330056@smail.nju.edu.cn> writes:

>> And if the test passed without exporting the variable, is it really
>> testing what we want to test?
>> 
>
> Sorry about that. I should put GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
> In front of `git rev-list …` so that when traverse bitmap it
> enters `pack-bitmap:find_boundary_objects()`.

That would work well.  By narrowing where the environment variable is
applied, such an arrangement would also help readers.

It still is curious why this version did not fail for you, though.
If setting it without exporting it still made "rev-list" traverse
and expected result, wouldn't that mean we are not really testing
what we want to test?

>>> + test_commit A &&
>>> + git repack -adb &&
>>> + test_commit B &&
>>> +
>>> + echo '1' >expect &&
>>> + git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
>>> + test_cmp expect actual
>>> + )
>>> +'
>>> +
>>> test_done
>>> 
>>> base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
>> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-03 15:14         ` Junio C Hamano
@ 2025-06-03 15:32           ` lidongyan
  2025-06-04 12:32             ` Junio C Hamano
  0 siblings, 1 reply; 28+ messages in thread
From: lidongyan @ 2025-06-03 15:32 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine

2025年6月3日 23:14,Junio C Hamano <gitster@pobox.com> 写道:
> 
> It still is curious why this version did not fail for you, though.
> If setting it without exporting it still made "rev-list" traverse
> and expected result, wouldn't that mean we are not really testing
> what we want to test?
> 
>>>> + test_commit A &&
>>>> + git repack -adb &&
>>>> + test_commit B &&
>>>> +
>>>> + echo '1' >expect &&
>>>> + git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
>>>> + test_cmp expect actual
>>>> + )
>>>> +'
>>>> +
>>>> test_done
>>>> 
>>>> base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
>>> 
> 

No, this test case should only fail when ’SANITIZE_LEAK’ is set. I heard
that other developer call this type of test as prereq. So only when git is
compiled with `-fsanitize=address` and `export ASAN_OPTION=detect_leaks=1`
and without changes as

- if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
- bitmap_free(roots_bitmap);
+ cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+ bitmap_free(roots_bitmap);

This test case would fail.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v4] pack-bitmap: remove checks before bitmap_free
  2025-06-03  6:20     ` [PATCH v4] " Lidong Yan via GitGitGadget
@ 2025-06-03 22:09       ` Taylor Blau
  2025-06-04  2:50         ` lidongyan
  2025-06-05  6:24       ` [PATCH v5] " Lidong Yan via GitGitGadget
  1 sibling, 1 reply; 28+ messages in thread
From: Taylor Blau @ 2025-06-03 22:09 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget
  Cc: git, Patrick Steinhardt, Eric Sunshine, Lidong Yan

On Tue, Jun 03, 2025 at 06:20:49AM +0000, Lidong Yan via GitGitGadget wrote:
> From: Lidong Yan <502024330056@smail.nju.edu.cn>
>
> In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
> if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
> use roots_bitmap as a mutable reference but not takes roots_bitmap's
> ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
> And this leak currently lacks a dedicated test to detect it.
>
> To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
> always calling bitmap_free(roots_bitmap);

This sentence might be more clear if it were written as:

    To fix this leak, unconditionally free the roots_bitmap regardless
    of whether or not cascade_pseudo_merges_1() succeeds.

> To trigger this leak, we need a pseudo-merge whose size is equal to
> or smaller than roots_bitmap (which corresponds to the set of "haves"
> commits in prepare_bitmap_walk()). To do this, we can create two
> commits: A and B. Add A to the pseudo-merge list and perform a traversal
> over the range A..B. In this scenario, the "haves" set will be {A},
> and cascade_pseudo_merges_1() will succeed, thereby exposing the leak
> due to the missing roots_bitmap cleanup.

I don't think this is quite right. Calling cascade_pseudo_merges_1()
succeeds (and returns a non-zero value) when one or more pseudo-merges
are satisfied. A pseudo-merge is satisfied here when its parents bitmap
is a *subset* of the roots_bitmap, not when it has a smaller size.

The precise definition of one bitmap being a subset of another can be
found in ewah/bitmap.c::ewah_bitamp_is_subset(). But in general one
bitmap is a subset of the other if the set of bit positions with value
"1" from one is a subset of the same set from the other bitmap.

I think that's what you meant by "smaller", but I think it's worth
clarifying here.

> diff --git a/pack-bitmap.c b/pack-bitmap.c
> index ac6d62b980c..8727f316de9 100644
> --- a/pack-bitmap.c
> +++ b/pack-bitmap.c
> @@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
>  			bitmap_set(roots_bitmap, pos);
>  		}
>
> -		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
> -			bitmap_free(roots_bitmap);
> +		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
> +		bitmap_free(roots_bitmap);

Makes sense.

> diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
> index 56674db562f..e665001a410 100755
> --- a/t/t5333-pseudo-merge-bitmaps.sh
> +++ b/t/t5333-pseudo-merge-bitmaps.sh
> @@ -445,4 +445,24 @@ test_expect_success 'pseudo-merge closure' '
>  	)
>  '
>
> +test_expect_success 'use pseudo-merge in boundary traversal' '
> +	git init pseudo-merge-boundary-traversal &&
> +	(
> +		cd pseudo-merge-boundary-traversal &&
> +
> +		git config bitmapPseudoMerge.test.pattern refs/ &&
> +		git config bitmapPseudoMerge.test.threshold now &&

Setting the unstable threshold here should be unnecessary, since the
unstable portion of the group only includes matching commits beyond the
threshold that *don't* already have a bitmap. Since "A" is the only
commit at the time you write the bitmap below, it will always be
selected, and thus never appear in the unstable portion of a
pseudo-merge group.

> +		git config bitmapPseudoMerge.test.stableThreshold now &&

This one is technically unnecessary, but only because test_commit starts
at the $test_tick value, which is very far in the past (beyond the
default value of 1.month.ago).

> +		test_commit A &&
> +		git repack -adb &&
> +		test_commit B &&
> +
> +		echo '1' >expect &&

Please do not use single-quotes in a test script. It happens to work in
this instance, but it is easy to break.

> +		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
> +			git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&

This test needs to use the boundary-based bitmap traversal routines, but
I'm unclear on why you're using the GIT_TEST_-environment variable to
enable them.

Is there a reason that we can't rely on the usual repository
configuration here? I would have expected something like this (which
should apply cleanly on top of your patch):

--- 8< ---
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index e665001a41..491ef404ea 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -453,14 +453,14 @@ test_expect_success 'use pseudo-merge in boundary traversal' '
 		git config bitmapPseudoMerge.test.pattern refs/ &&
 		git config bitmapPseudoMerge.test.threshold now &&
 		git config bitmapPseudoMerge.test.stableThreshold now &&
+		git config pack.useBitmapBoundaryTraversal true &&

 		test_commit A &&
 		git repack -adb &&
 		test_commit B &&

-		echo '1' >expect &&
-		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
-			git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
+		echo 1 >expect &&
+		git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
 		test_cmp expect actual
 	)
 '
--- >8 ---

> +		test_cmp expect actual

Hmm. I suppose, although it feels a little clunky to me to write
something like "echo 1 >expect". I would imagine that you'd do something
like:

    test 1 -eq $(git rev-list --count --use-bitmap-index HEAD~1..HEAD)

instead. Or if you wanted to split them off into separate lines, you
could do:

    nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
    test 1 -eq "$nr"

Thanks,
Taylor

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v4] pack-bitmap: remove checks before bitmap_free
  2025-06-03 22:09       ` Taylor Blau
@ 2025-06-04  2:50         ` lidongyan
  0 siblings, 0 replies; 28+ messages in thread
From: lidongyan @ 2025-06-04  2:50 UTC (permalink / raw)
  To: Taylor Blau
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine



> 2025年6月4日 06:09,Taylor Blau <me@ttaylorr.com> 写道:
> 
> On Tue, Jun 03, 2025 at 06:20:49AM +0000, Lidong Yan via GitGitGadget wrote:
>> From: Lidong Yan <502024330056@smail.nju.edu.cn>
>> 
>> In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
>> if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
>> use roots_bitmap as a mutable reference but not takes roots_bitmap's
>> ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
>> And this leak currently lacks a dedicated test to detect it.
>> 
>> To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
>> always calling bitmap_free(roots_bitmap);
> 
> This sentence might be more clear if it were written as:
> 
>    To fix this leak, unconditionally free the roots_bitmap regardless
>    of whether or not cascade_pseudo_merges_1() succeeds.
> 
>> To trigger this leak, we need a pseudo-merge whose size is equal to
>> or smaller than roots_bitmap (which corresponds to the set of "haves"
>> commits in prepare_bitmap_walk()). To do this, we can create two
>> commits: A and B. Add A to the pseudo-merge list and perform a traversal
>> over the range A..B. In this scenario, the "haves" set will be {A},
>> and cascade_pseudo_merges_1() will succeed, thereby exposing the leak
>> due to the missing roots_bitmap cleanup.
> 
> I don't think this is quite right. Calling cascade_pseudo_merges_1()
> succeeds (and returns a non-zero value) when one or more pseudo-merges
> are satisfied. A pseudo-merge is satisfied here when its parents bitmap
> is a *subset* of the roots_bitmap, not when it has a smaller size.
> 
> The precise definition of one bitmap being a subset of another can be
> found in ewah/bitmap.c::ewah_bitamp_is_subset(). But in general one
> bitmap is a subset of the other if the set of bit positions with value
> "1" from one is a subset of the same set from the other bitmap.
> 
> I think that's what you meant by "smaller", but I think it's worth
> clarifying here.

Yes, I want to say subset here, I will rewrite this part of comment.

> 
>> diff --git a/pack-bitmap.c b/pack-bitmap.c
>> index ac6d62b980c..8727f316de9 100644
>> --- a/pack-bitmap.c
>> +++ b/pack-bitmap.c
>> @@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
>> bitmap_set(roots_bitmap, pos);
>> }
>> 
>> - if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
>> - bitmap_free(roots_bitmap);
>> + cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
>> + bitmap_free(roots_bitmap);
> 
> Makes sense.
> 
>> diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
>> index 56674db562f..e665001a410 100755
>> --- a/t/t5333-pseudo-merge-bitmaps.sh
>> +++ b/t/t5333-pseudo-merge-bitmaps.sh
>> @@ -445,4 +445,24 @@ test_expect_success 'pseudo-merge closure' '
>> )
>> '
>> 
>> +test_expect_success 'use pseudo-merge in boundary traversal' '
>> + git init pseudo-merge-boundary-traversal &&
>> + (
>> + cd pseudo-merge-boundary-traversal &&
>> +
>> + git config bitmapPseudoMerge.test.pattern refs/ &&
>> + git config bitmapPseudoMerge.test.threshold now &&
> 
> Setting the unstable threshold here should be unnecessary, since the
> unstable portion of the group only includes matching commits beyond the
> threshold that *don't* already have a bitmap. Since "A" is the only
> commit at the time you write the bitmap below, it will always be
> selected, and thus never appear in the unstable portion of a
> pseudo-merge group.
> 
>> + git config bitmapPseudoMerge.test.stableThreshold now &&
> 
> This one is technically unnecessary, but only because test_commit starts
> at the $test_tick value, which is very far in the past (beyond the
> default value of 1.month.ago).

May be this is the time for me to re-read pseudo-merge documents.

> 
>> + test_commit A &&
>> + git repack -adb &&
>> + test_commit B &&
>> +
>> + echo '1' >expect &&
> 
> Please do not use single-quotes in a test script. It happens to work in
> this instance, but it is easy to break.

Got it.

> 
>> + GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
>> + git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
> 
> This test needs to use the boundary-based bitmap traversal routines, but
> I'm unclear on why you're using the GIT_TEST_-environment variable to
> enable them.

I don’t have a special reason to choose GIT_TEST rather than `git config`.
I just find in both way this test works so I use GIT_TEST. I will switch to `git config`.

>  
> Is there a reason that we can't rely on the usual repository
> configuration here? I would have expected something like this (which
> should apply cleanly on top of your patch):
> 
> --- 8< ---
> diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
> index e665001a41..491ef404ea 100755
> --- a/t/t5333-pseudo-merge-bitmaps.sh
> +++ b/t/t5333-pseudo-merge-bitmaps.sh
> @@ -453,14 +453,14 @@ test_expect_success 'use pseudo-merge in boundary traversal' '
> git config bitmapPseudoMerge.test.pattern refs/ &&
> git config bitmapPseudoMerge.test.threshold now &&
> git config bitmapPseudoMerge.test.stableThreshold now &&
> + git config pack.useBitmapBoundaryTraversal true &&
> 
> test_commit A &&
> git repack -adb &&
> test_commit B &&
> 
> - echo '1' >expect &&
> - GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
> - git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
> + echo 1 >expect &&
> + git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
> test_cmp expect actual
> )
> '
> --- >8 ---
> 
>> + test_cmp expect actual
> 
> Hmm. I suppose, although it feels a little clunky to me to write
> something like "echo 1 >expect". I would imagine that you'd do something
> like:
> 
>    test 1 -eq $(git rev-list --count --use-bitmap-index HEAD~1..HEAD)
> 
> instead. Or if you wanted to split them off into separate lines, you
> could do:
> 
>    nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
>    test 1 -eq "$nr"
> 

I like the latter one, I will use it in the next series.

Thanks,
Lidong


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-03 15:32           ` lidongyan
@ 2025-06-04 12:32             ` Junio C Hamano
  2025-06-04 12:43               ` lidongyan
  0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2025-06-04 12:32 UTC (permalink / raw)
  To: lidongyan
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine

lidongyan <502024330056@smail.nju.edu.cn> writes:

> No, this test case should only fail when ’SANITIZE_LEAK’ is set. I heard
> that other developer call this type of test as prereq. So only when git is
> compiled with `-fsanitize=address` and `export ASAN_OPTION=detect_leaks=1`
> and without changes as
>
> - if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
> - bitmap_free(roots_bitmap);
> + cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
> + bitmap_free(roots_bitmap);
>
> This test case would fail.

If the test tickles the code path that used to be broken (and
corrected by the patch), temporarily reverting only the code changes
to pack-bitmap.c and then this test (under leak sanitizer, of
course) should have failed.  And if the test passed with such an
experiment, you would have noticed that something is wrong.

But you didn't notice it and sent the patch, so I'd assume that you
saw such a test still failed.  IOW, with "export" forgotten in the
test, the original (unfixed) code still leaked, without using the
bitmap traversal, right?

Which was where my question came from.

Or perhaps you didn't do that "is my test really tickling the bug I
fixed and makes the original code without my fix fail?" test?  Which
also explains why lack of "export" was not noticed.


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-04 12:32             ` Junio C Hamano
@ 2025-06-04 12:43               ` lidongyan
  2025-06-04 14:49                 ` Junio C Hamano
  0 siblings, 1 reply; 28+ messages in thread
From: lidongyan @ 2025-06-04 12:43 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine

2025年6月4日 20:32,Junio C Hamano <gitster@pobox.com> 写道:
> 
> lidongyan <502024330056@smail.nju.edu.cn> writes:
> 
>> No, this test case should only fail when ’SANITIZE_LEAK’ is set. I heard
>> that other developer call this type of test as prereq. So only when git is
>> compiled with `-fsanitize=address` and `export ASAN_OPTION=detect_leaks=1`
>> and without changes as
>> 
>> - if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
>> - bitmap_free(roots_bitmap);
>> + cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
>> + bitmap_free(roots_bitmap);
>> 
>> This test case would fail.
> 
> If the test tickles the code path that used to be broken (and
> corrected by the patch), temporarily reverting only the code changes
> to pack-bitmap.c and then this test (under leak sanitizer, of
> course) should have failed.  And if the test passed with such an
> experiment, you would have noticed that something is wrong.
> 
> But you didn't notice it and sent the patch, so I'd assume that you
> saw such a test still failed.  IOW, with "export" forgotten in the
> test, the original (unfixed) code still leaked, without using the
> bitmap traversal, right?
> 
> Which was where my question came from.
> 
> Or perhaps you didn't do that "is my test really tickling the bug I
> fixed and makes the original code without my fix fail?" test?  Which
> also explains why lack of "export" was not noticed.

The test case in v0 with “export” would fail, but test-lint in CI shouts. To make
CI happy, I delete “export” and submit immediately. So I am sure now in
v3 this test truly test what we want. But I make two mistakes that I haven’t
pass all CI test before submit. I apologize for the oversight. I'll double-check
my tests more carefully in the future to avoid similar issues.

Thanks,
Lidong

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v3] pack-bitmap: remove checks before bitmap_free
  2025-06-04 12:43               ` lidongyan
@ 2025-06-04 14:49                 ` Junio C Hamano
  0 siblings, 0 replies; 28+ messages in thread
From: Junio C Hamano @ 2025-06-04 14:49 UTC (permalink / raw)
  To: lidongyan
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine

lidongyan <502024330056@smail.nju.edu.cn> writes:

>> Which was where my question came from.
>> 
>> Or perhaps you didn't do that "is my test really tickling the bug I
>> fixed and makes the original code without my fix fail?" test?  Which
>> also explains why lack of "export" was not noticed.
>
> The test case in v0 with “export” would fail, but test-lint in CI shouts. To make
> CI happy, I delete “export” and submit immediately. So I am sure now in
> v3 this test truly test what we want. But I make two mistakes that I haven’t
> pass all CI test before submit. I apologize for the oversight. I'll double-check
> my tests more carefully in the future to avoid similar issues.

Ah, I understood what happened.  No need to apologize.  I just
wanted to know how it was missed, so that we all can learn from
this episode to make sure that our tests do verify what we want
them to.

Thanks.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v5] pack-bitmap: remove checks before bitmap_free
  2025-06-03  6:20     ` [PATCH v4] " Lidong Yan via GitGitGadget
  2025-06-03 22:09       ` Taylor Blau
@ 2025-06-05  6:24       ` Lidong Yan via GitGitGadget
  2025-06-05 15:29         ` Junio C Hamano
  2025-06-05 15:53         ` [PATCH v6] " Lidong Yan via GitGitGadget
  1 sibling, 2 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-06-05  6:24 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Eric Sunshine, Taylor Blau, Lidong Yan,
	Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
use roots_bitmap as a mutable reference but not takes roots_bitmap's
ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
And this leak currently lacks a dedicated test to detect it.

To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
always calling bitmap_free(roots_bitmap);

To trigger this leak, we need roots_bitmap contains at least one pseudo
merge. So that we can use pseudo merge bitmap when we compute roots
reachable bitmap. Here we create two commits: first A then B. Add A
to the pseudo-merge and perform a traversal over the range A..B.
In this scenario, the "haves" set will be {A}, and cascade_pseudo_merges_1
will succeed, thereby exposing the leak due to the missing roots_bitmap
cleanup.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
    pack-bitmap: remove checks before bitmap_free
    
    In pack-bitmap.c:find_boundary_objects, remove cascade success check and
    always free roots_bitmap afterward to make static analysis tool works
    better.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v5
Pull-Request: https://github.com/git/git/pull/1977

Range-diff vs v4:

 1:  fa443065436 ! 1:  4bc90c83a40 pack-bitmap: remove checks before bitmap_free
     @@ Commit message
          To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
          always calling bitmap_free(roots_bitmap);
      
     -    To trigger this leak, we need a pseudo-merge whose size is equal to
     -    or smaller than roots_bitmap (which corresponds to the set of "haves"
     -    commits in prepare_bitmap_walk()). To do this, we can create two
     -    commits: A and B. Add A to the pseudo-merge list and perform a traversal
     -    over the range A..B. In this scenario, the "haves" set will be {A},
     -    and cascade_pseudo_merges_1() will succeed, thereby exposing the leak
     -    due to the missing roots_bitmap cleanup.
     +    To trigger this leak, we need roots_bitmap contains at least one pseudo
     +    merge. So that we can use pseudo merge bitmap when we compute roots
     +    reachable bitmap. Here we create two commits: first A then B. Add A
     +    to the pseudo-merge and perform a traversal over the range A..B.
     +    In this scenario, the "haves" set will be {A}, and cascade_pseudo_merges_1
     +    will succeed, thereby exposing the leak due to the missing roots_bitmap
     +    cleanup.
      
          Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
      
     @@ t/t5333-pseudo-merge-bitmaps.sh: test_expect_success 'pseudo-merge closure' '
      +		cd pseudo-merge-boundary-traversal &&
      +
      +		git config bitmapPseudoMerge.test.pattern refs/ &&
     -+		git config bitmapPseudoMerge.test.threshold now &&
     -+		git config bitmapPseudoMerge.test.stableThreshold now &&
     ++		git config pack.useBitmapBoundaryTraversal true &&
      +
      +		test_commit A &&
      +		git repack -adb &&
      +		test_commit B &&
      +
     -+		echo '1' >expect &&
     -+		GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 \
     -+			git rev-list --count --use-bitmap-index HEAD~1..HEAD >actual &&
     -+		test_cmp expect actual
     ++		nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
     ++		test 1 -eq "$nr"
      +	)
      +'
      +


 pack-bitmap.c                   |  4 ++--
 t/t5333-pseudo-merge-bitmaps.sh | 17 +++++++++++++++++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c..8727f316de9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 56674db562f..ba5ae6a00c9 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -445,4 +445,21 @@ test_expect_success 'pseudo-merge closure' '
 	)
 '
 
+test_expect_success 'use pseudo-merge in boundary traversal' '
+	git init pseudo-merge-boundary-traversal &&
+	(
+		cd pseudo-merge-boundary-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/ &&
+		git config pack.useBitmapBoundaryTraversal true &&
+
+		test_commit A &&
+		git repack -adb &&
+		test_commit B &&
+
+		nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
+		test 1 -eq "$nr"
+	)
+'
+
 test_done

base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v5] pack-bitmap: remove checks before bitmap_free
  2025-06-05  6:24       ` [PATCH v5] " Lidong Yan via GitGitGadget
@ 2025-06-05 15:29         ` Junio C Hamano
  2025-06-10  5:58           ` lidongyan
  2025-06-05 15:53         ` [PATCH v6] " Lidong Yan via GitGitGadget
  1 sibling, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2025-06-05 15:29 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget
  Cc: git, Patrick Steinhardt, Eric Sunshine, Taylor Blau, Lidong Yan

"Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Lidong Yan <502024330056@smail.nju.edu.cn>
>
> In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
> if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
> use roots_bitmap as a mutable reference but not takes roots_bitmap's
> ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.

"Once cascade_pseudo_merges_1() succeeds", perhaps?

> And this leak currently lacks a dedicated test to detect it.
>
> To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
> always calling bitmap_free(roots_bitmap);
>
> To trigger this leak, we need roots_bitmap contains at least one pseudo
> merge.

"contains" -> "that contains"?

> diff --git a/pack-bitmap.c b/pack-bitmap.c
> index ac6d62b980c..8727f316de9 100644
> --- a/pack-bitmap.c
> +++ b/pack-bitmap.c
> @@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
>  			bitmap_set(roots_bitmap, pos);
>  		}
>  
> -		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
> -			bitmap_free(roots_bitmap);
> +		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
> +		bitmap_free(roots_bitmap);

This makes it as if the original _wanted_ to leak it when the call
failed.  Readers may wonder how we got into this state in the first
place.  Was it a simple thinko when 11d45a6e (pack-bitmap.c: use
pseudo-merges during traversal, 2024-05-23) was written, I have to
wonder.

> diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
> index 56674db562f..ba5ae6a00c9 100755
> --- a/t/t5333-pseudo-merge-bitmaps.sh
> +++ b/t/t5333-pseudo-merge-bitmaps.sh
> @@ -445,4 +445,21 @@ test_expect_success 'pseudo-merge closure' '
>  	)
>  '
>  
> +test_expect_success 'use pseudo-merge in boundary traversal' '
> +	git init pseudo-merge-boundary-traversal &&
> +	(
> +		cd pseudo-merge-boundary-traversal &&
> +
> +		git config bitmapPseudoMerge.test.pattern refs/ &&
> +		git config pack.useBitmapBoundaryTraversal true &&

Yup, this is a temporary repository for only this test, so using
"git config" there makes it the simplest and the easiest to
understand.

> +		test_commit A &&
> +		git repack -adb &&
> +		test_commit B &&
> +
> +		nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
> +		test 1 -eq "$nr"
> +	)
> +'
> +
>  test_done
>
> base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v6] pack-bitmap: remove checks before bitmap_free
  2025-06-05  6:24       ` [PATCH v5] " Lidong Yan via GitGitGadget
  2025-06-05 15:29         ` Junio C Hamano
@ 2025-06-05 15:53         ` Lidong Yan via GitGitGadget
  2025-06-06  1:28           ` Junio C Hamano
  2025-06-09  8:18           ` [PATCH v7] " Lidong Yan via GitGitGadget
  1 sibling, 2 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-06-05 15:53 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Eric Sunshine, Taylor Blau, Lidong Yan,
	Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
use roots_bitmap as a mutable reference but not takes roots_bitmap's
ownership. Once cascade_pseudo_merges_1() succeeds, roots_bitmap leaks.
And this leak currently lacks a dedicated test to detect it.

To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
always calling bitmap_free(roots_bitmap);

To trigger this leak, we need roots_bitmap that contains at least one
pseudo merge. So that we can use pseudo merge bitmap when we compute roots
reachable bitmap. Here we create two commits: first A then B. Add A
to the pseudo-merge and perform a traversal over the range A..B.
In this scenario, the "haves" set will be {A}, and cascade_pseudo_merges_1
will succeed, thereby exposing the leak due to the missing roots_bitmap
cleanup.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
    pack-bitmap: remove checks before bitmap_free
    
    In pack-bitmap.c:find_boundary_objects, remove cascade success check and
    always free roots_bitmap afterward to make static analysis tool works
    better.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v6
Pull-Request: https://github.com/git/git/pull/1977

Range-diff vs v5:

 1:  4bc90c83a40 ! 1:  43cdce190dc pack-bitmap: remove checks before bitmap_free
     @@ Commit message
          In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
          if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
          use roots_bitmap as a mutable reference but not takes roots_bitmap's
     -    ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
     +    ownership. Once cascade_pseudo_merges_1() succeeds, roots_bitmap leaks.
          And this leak currently lacks a dedicated test to detect it.
      
          To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
          always calling bitmap_free(roots_bitmap);
      
     -    To trigger this leak, we need roots_bitmap contains at least one pseudo
     -    merge. So that we can use pseudo merge bitmap when we compute roots
     +    To trigger this leak, we need roots_bitmap that contains at least one
     +    pseudo merge. So that we can use pseudo merge bitmap when we compute roots
          reachable bitmap. Here we create two commits: first A then B. Add A
          to the pseudo-merge and perform a traversal over the range A..B.
          In this scenario, the "haves" set will be {A}, and cascade_pseudo_merges_1


 pack-bitmap.c                   |  4 ++--
 t/t5333-pseudo-merge-bitmaps.sh | 17 +++++++++++++++++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c..8727f316de9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 56674db562f..ba5ae6a00c9 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -445,4 +445,21 @@ test_expect_success 'pseudo-merge closure' '
 	)
 '
 
+test_expect_success 'use pseudo-merge in boundary traversal' '
+	git init pseudo-merge-boundary-traversal &&
+	(
+		cd pseudo-merge-boundary-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/ &&
+		git config pack.useBitmapBoundaryTraversal true &&
+
+		test_commit A &&
+		git repack -adb &&
+		test_commit B &&
+
+		nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
+		test 1 -eq "$nr"
+	)
+'
+
 test_done

base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v6] pack-bitmap: remove checks before bitmap_free
  2025-06-05 15:53         ` [PATCH v6] " Lidong Yan via GitGitGadget
@ 2025-06-06  1:28           ` Junio C Hamano
  2025-06-06  5:49             ` lidongyan
  2025-06-09  8:18           ` [PATCH v7] " Lidong Yan via GitGitGadget
  1 sibling, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2025-06-06  1:28 UTC (permalink / raw)
  To: Lidong Yan via GitGitGadget
  Cc: git, Patrick Steinhardt, Eric Sunshine, Taylor Blau, Lidong Yan

"Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Lidong Yan <502024330056@smail.nju.edu.cn>
>
> In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
> if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
> use roots_bitmap as a mutable reference but not takes roots_bitmap's
> ownership.

Sorry but I cannot parse the last sentence above.  I would have
expected that "Since/Because X" to be followed by comma and a
sentence that describes the consequence of X.  Also "but not takes"
-> "but does not take", probably.


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v6] pack-bitmap: remove checks before bitmap_free
  2025-06-06  1:28           ` Junio C Hamano
@ 2025-06-06  5:49             ` lidongyan
  0 siblings, 0 replies; 28+ messages in thread
From: lidongyan @ 2025-06-06  5:49 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine, Taylor Blau

2025年6月6日 09:28,Junio C Hamano <gitster@pobox.com> 写道:
> 
> "Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>> From: Lidong Yan <502024330056@smail.nju.edu.cn>
>> 
>> In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
>> if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
>> use roots_bitmap as a mutable reference but not takes roots_bitmap's
>> ownership.
> 
> Sorry but I cannot parse the last sentence above.  I would have
> expected that "Since/Because X" to be followed by comma and a
> sentence that describes the consequence of X.  Also "but not takes"
> -> "but does not take", probably.


You are right, I should use a grammar checker (chatgpt) on my log message.
How about
“
Since cascade_pseudo_merges_1() only
use roots_bitmap as a mutable reference but not takes roots_bitmap's
ownership. Once cascade_pseudo_merges_1() succeeds, roots_bitmap leaks.
”
->
“
However, cascade_pseudo_merges_1() uses roots_bitmap as a 
mutable reference without taking ownership of it. As a result, if 
cascade_pseudo_merges_1() succeeds, roots_bitmap is leaked.
”

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v7] pack-bitmap: remove checks before bitmap_free
  2025-06-05 15:53         ` [PATCH v6] " Lidong Yan via GitGitGadget
  2025-06-06  1:28           ` Junio C Hamano
@ 2025-06-09  8:18           ` Lidong Yan via GitGitGadget
  1 sibling, 0 replies; 28+ messages in thread
From: Lidong Yan via GitGitGadget @ 2025-06-09  8:18 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Eric Sunshine, Taylor Blau, Lidong Yan,
	Lidong Yan

From: Lidong Yan <502024330056@smail.nju.edu.cn>

In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
if cascade_pseudo_merges_1() fails. However, cascade_pseudo_merges_1()
uses roots_bitmap as a mutable reference without taking ownership of it.
As a result, if cascade_pseudo_merges_1() succeeds, roots_bitmap is leaked.
And this leak currently lacks a dedicated test to detect it.

To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
always calling bitmap_free(roots_bitmap);

To trigger this leak, we need roots_bitmap that contains at least one
pseudo merge. So that we can use pseudo merge bitmap when we compute roots
reachable bitmap. Here we create two commits: first A then B. Add A
to the pseudo-merge and perform a traversal over the range A..B.
In this scenario, the "haves" set will be {A}, and cascade_pseudo_merges_1
will succeed, thereby exposing the leak due to the missing roots_bitmap
cleanup.

Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn>
---
    pack-bitmap: remove checks before bitmap_free
    
    In pack-bitmap.c:find_boundary_objects, remove cascade success check and
    always free roots_bitmap afterward to make static analysis tool works
    better.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1977%2Fbrandb97%2Fremove-check-before-bitmap-free-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1977/brandb97/remove-check-before-bitmap-free-v7
Pull-Request: https://github.com/git/git/pull/1977

Range-diff vs v6:

 1:  43cdce190dc ! 1:  74c41eccfb0 pack-bitmap: remove checks before bitmap_free
     @@ Commit message
          pack-bitmap: remove checks before bitmap_free
      
          In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
     -    if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
     -    use roots_bitmap as a mutable reference but not takes roots_bitmap's
     -    ownership. Once cascade_pseudo_merges_1() succeeds, roots_bitmap leaks.
     +    if cascade_pseudo_merges_1() fails. However, cascade_pseudo_merges_1()
     +    uses roots_bitmap as a mutable reference without taking ownership of it.
     +    As a result, if cascade_pseudo_merges_1() succeeds, roots_bitmap is leaked.
          And this leak currently lacks a dedicated test to detect it.
      
          To fix this leak, remove if cascade_pseudo_merges_1() succeed check and


 pack-bitmap.c                   |  4 ++--
 t/t5333-pseudo-merge-bitmaps.sh | 17 +++++++++++++++++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/pack-bitmap.c b/pack-bitmap.c
index ac6d62b980c..8727f316de9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
 			bitmap_set(roots_bitmap, pos);
 		}
 
-		if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
-			bitmap_free(roots_bitmap);
+		cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
+		bitmap_free(roots_bitmap);
 	}
 
 	/*
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 56674db562f..ba5ae6a00c9 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -445,4 +445,21 @@ test_expect_success 'pseudo-merge closure' '
 	)
 '
 
+test_expect_success 'use pseudo-merge in boundary traversal' '
+	git init pseudo-merge-boundary-traversal &&
+	(
+		cd pseudo-merge-boundary-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/ &&
+		git config pack.useBitmapBoundaryTraversal true &&
+
+		test_commit A &&
+		git repack -adb &&
+		test_commit B &&
+
+		nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
+		test 1 -eq "$nr"
+	)
+'
+
 test_done

base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v5] pack-bitmap: remove checks before bitmap_free
  2025-06-05 15:29         ` Junio C Hamano
@ 2025-06-10  5:58           ` lidongyan
  0 siblings, 0 replies; 28+ messages in thread
From: lidongyan @ 2025-06-10  5:58 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Lidong Yan via GitGitGadget, git, Patrick Steinhardt,
	Eric Sunshine, Taylor Blau

Junio C Hamano <gitster@pobox.com> 写道:
> 
> "Lidong Yan via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>> From: Lidong Yan <502024330056@smail.nju.edu.cn>
>> 
>> In pack-bitmap.c:find_boundary_objects(), the roots_bitmap is only freed
>> if cascade_pseudo_merges_1() fails. Since cascade_pseudo_merges_1() only
>> use roots_bitmap as a mutable reference but not takes roots_bitmap's
>> ownership. Once cascade_pseudo_merges_1 succeed(), roots_bitmap leaks.
> 
> "Once cascade_pseudo_merges_1() succeeds", perhaps?
> 
>> And this leak currently lacks a dedicated test to detect it.
>> 
>> To fix this leak, remove if cascade_pseudo_merges_1() succeed check and
>> always calling bitmap_free(roots_bitmap);
>> 
>> To trigger this leak, we need roots_bitmap contains at least one pseudo
>> merge.
> 
> "contains" -> "that contains"?
> 
>> diff --git a/pack-bitmap.c b/pack-bitmap.c
>> index ac6d62b980c..8727f316de9 100644
>> --- a/pack-bitmap.c
>> +++ b/pack-bitmap.c
>> @@ -1363,8 +1363,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
>> bitmap_set(roots_bitmap, pos);
>> }
>> 
>> - if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
>> - bitmap_free(roots_bitmap);
>> + cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap);
>> + bitmap_free(roots_bitmap);
> 
> This makes it as if the original _wanted_ to leak it when the call
> failed.  Readers may wonder how we got into this state in the first
> place.  Was it a simple thinko when 11d45a6e (pack-bitmap.c: use
> pseudo-merges during traversal, 2024-05-23) was written, I have to
> wonder.

I think this was a simple thinko, similar to "we need to free resources if
something fails. Since cascade_pseudo_merges_1() fails, we need to free roots_bitmap”.
In commit 55e563a (pseudo-merge: fix various memory leaks, 2024-09-30),
Patrick fixed a similar leak in find_objects(). However, since t5333 doesn’t
test boundary traversal, the leak in find_boundary_objects() remains unresolved.


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2025-06-10  5:59 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-25  5:09 [PATCH] pack-bitmap: remove checks before bitmap_free Lidong Yan via GitGitGadget
2025-05-26  6:49 ` Patrick Steinhardt
2025-05-26 16:05   ` lidongyan
2025-05-30 18:14 ` [PATCH v2 0/2] " Lidong Yan via GitGitGadget
2025-05-30 18:14   ` [PATCH v2 1/2] " Lidong Yan via GitGitGadget
2025-05-30 18:14   ` [PATCH v2 2/2] t5333: test memory leak when use pseudo-merge in boundary traversal Lidong Yan via GitGitGadget
2025-05-30 21:42     ` Junio C Hamano
2025-05-30 21:50       ` Eric Sunshine
2025-05-31  3:18         ` lidongyan
2025-05-30 21:06   ` [PATCH v2 0/2] pack-bitmap: remove checks before bitmap_free Junio C Hamano
2025-06-03  1:46   ` [PATCH v3] " Lidong Yan via GitGitGadget
2025-06-03  6:12     ` Junio C Hamano
2025-06-03  6:22       ` lidongyan
2025-06-03 15:14         ` Junio C Hamano
2025-06-03 15:32           ` lidongyan
2025-06-04 12:32             ` Junio C Hamano
2025-06-04 12:43               ` lidongyan
2025-06-04 14:49                 ` Junio C Hamano
2025-06-03  6:20     ` [PATCH v4] " Lidong Yan via GitGitGadget
2025-06-03 22:09       ` Taylor Blau
2025-06-04  2:50         ` lidongyan
2025-06-05  6:24       ` [PATCH v5] " Lidong Yan via GitGitGadget
2025-06-05 15:29         ` Junio C Hamano
2025-06-10  5:58           ` lidongyan
2025-06-05 15:53         ` [PATCH v6] " Lidong Yan via GitGitGadget
2025-06-06  1:28           ` Junio C Hamano
2025-06-06  5:49             ` lidongyan
2025-06-09  8:18           ` [PATCH v7] " Lidong Yan via GitGitGadget

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).