git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 0/9] commit-reach: -Wsign-compare follow-ups

From
Jeff King <peff@peff.net>
Date
Dec 27, 2024, 21:37 UTC
Message-ID
<20241227213729.GA796141@coredump.intra.peff.net>
In-Reply-To
<xmqq7c7kubx8.fsf@gitster.g>
On Fri, Dec 27, 2024 at 12:08:03PM -0800, Junio C Hamano wrote:
Show 38 quoted lines
> Junio C Hamano <gitster@pobox.com> writes:
> 
> > Error: shallow.c:537:32: comparison of integer expressions of different signedness:
> > 'unsigned int' and 'int' [-Werror=sign-compare]
> >
> >     537 |  if (!info->pool_count || size > info->end - info->free) {
> >
> > I didn't dig deeper than this.
> 
> What we want to express seems to be:
> 
>     If the region between "end" and "free" is smaller than the
>     required "size" then we are in trouble.
> 
> which can be said more naturally with
> 
>     If the region of size "size" starting at "free" pointer overruns the
>     "end" pointer, we are in trouble.
> 
> So perhaps something like this would help?  We are no longer making
> a comparison between two integers with this rewrite.
> 
>  shallow.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git c/shallow.c w/shallow.c
> index b8fcfbef0f..b54244ffa9 100644
> --- c/shallow.c
> +++ w/shallow.c
> @@ -534,7 +534,7 @@ static uint32_t *paint_alloc(struct paint_info *info)
>  	unsigned nr = DIV_ROUND_UP(info->nr_bits, 32);
>  	unsigned size = nr * sizeof(uint32_t);
>  	void *p;
> -	if (!info->pool_count || size > info->end - info->free) {
> +	if (!info->pool_count || info->end < info->free + size) {
>  		if (size > POOL_SIZE)
>  			BUG("pool size too small for %d in paint_alloc()",
>  			    size);

I doubt it is a practical problem in this instance, but as a general rule, I think bounds checks like this should avoid using addition because it can lead to overflow. In this code, imagine that the free pool was allocated near the end of the address space, but somebody asked for a very large "size", causing the addition to wrap around.

I think even without that overflow this might be undefined behavior, because "info->free + size" is forming a pointer that may be outside the allocated array.

So as a general rule, bounds comparisons like this should be formulated as "how much free space do we have" compared to "how much are we asking for". And there "info->end - info->free" is the right way to ask the first half of that question.

Now where it gets weird with -Wsign-compare is that the pointer difference is giving us a signed value. Which makes sense in the general case (you could ask for "info->free - info->end", for example). But we know that it will always be non-negative because we know that info->free is <= info->end, coming from earlier in the same allocation.

I doubt there is a way to tell the compiler that (or that a compiler could even switch to an unsigned ptrdiff type if it knew that). But I wonder if there is a generalized helper we can devise that would avoid simply casting here. I guess that could be a checked cast like:

  static inline size_t ptrdiff_to_size(ptrdiff_t v)
  {
	if (v < 0)
		BUG("surprising negative value: %"PRIdMAX, v);
	return (size_t)v;
  }
or even:
  static inline bool has_space(const void *vs, const void *ve, size_t want)
  {
	const char *s = vs, e = ve;
	return want <= ptrdiff_to_size(ve - vs);
  }

I don't love hiding basic things like this behind macros or inlines. But allocation and bounds comparisons do have gotchas (especially against an adversary that can try to create pathological situations). Maybe it's worth having an easy way to do them safely without having to think about each one. I dunno.

-Peff
Previous: Junio C HamanoNext: Patrick Steinhardt
Message 23 of 34 in “commit-reach: -Wsign-compare follow-ups”
  1. 0/9 commit-reach: -Wsign-compare follow-upsPatrick Steinhardt, Dec 27, 2024
  2. 1/9 prio-queue: fix type of `insertion_ctr`Patrick Steinhardt, Dec 27, 2024
  3. Jeff KingDec 27, 2024
  4. 2/9 commit-reach: fix index used to loop through unsigned integerPatrick Steinhardt, Dec 27, 2024
  5. Jeff KingDec 27, 2024
  6. 3/9 commit-reach: fix type of `min_commit_date`Patrick Steinhardt, Dec 27, 2024
  7. 4/9 commit-reach: use `size_t` to track indices in `remove_redundant()`Patrick Steinhardt, Dec 27, 2024
  8. Justin ToblerJan 3, 2025
  9. 5/9 commit-reach: use `size_t` to track indices in `get_reachable_subset()`Patrick Steinhardt, Dec 27, 2024
  10. 6/9 builtin/log: use `size_t` to track indicesPatrick Steinhardt, Dec 27, 2024
  11. Justin ToblerJan 3, 2025
  12. Patrick SteinhardtJan 3, 2025
  13. 7/9 builtin/log: fix remaining -Wsign-compare warningsPatrick Steinhardt, Dec 27, 2024
  14. shejialuoDec 27, 2024
  15. Patrick SteinhardtDec 27, 2024
  16. shejialuoDec 27, 2024
  17. 8/9 shallow: fix -Wsign-compare warningsPatrick Steinhardt, Dec 27, 2024
  18. 9/9 commit-reach: use `size_t` to track indices when computing merge basesPatrick Steinhardt, Dec 27, 2024
  19. Justin ToblerJan 3, 2025
  20. Patrick SteinhardtJan 3, 2025
  21. Junio C HamanoDec 27, 2024
  22. Junio C HamanoDec 27, 2024
  23. Jeff KingDec 27, 2024
  24. Patrick SteinhardtDec 28, 2024
  25. Junio C HamanoDec 28, 2024
  26. Patrick SteinhardtDec 28, 2024
  27. Junio C HamanoDec 28, 2024
  28. racy leak sanitizer builds, was Re: [PATCH 0/9] commit-reach: -Wsign-compare follow-upsJeff King, Dec 28, 2024
  29. Jeff KingDec 28, 2024
  30. Jeff KingDec 28, 2024
  31. René ScharfeDec 29, 2024
  32. Jeff KingDec 29, 2024
  33. Jeff KingDec 30, 2024
  34. Junio C HamanoDec 30, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.