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

Re: [PATCH 00/12] Clean up notes-related code around `load_subtree()`

From
Jeff King <peff@peff.net>
Date
Sep 9, 2017, 10:31 UTC
Message-ID
<20170909103131.pppm346qbj2cdxuo@sigill.intra.peff.net>
In-Reply-To
<cover.1503734566.git.mhagger@alum.mit.edu>
On Sat, Aug 26, 2017 at 10:28:00AM +0200, Michael Haggerty wrote:
Show 13 quoted lines
> It turns out that the comment is incorrect, but there was nevertheless
> plenty that could be cleaned up in the area:
> 
> * Make macro `GIT_NIBBLE` safer by adding some parentheses
> * Remove some dead code
> * Fix some memory leaks
> * Fix some obsolete and incorrect comments
> * Reject "notes" that are not blobs
> 
> I hope the result is also easier to understand.
> 
> This branch is also available from my Git fork [1] as branch
> `load-subtree-cleanup`.

FYI, Coverity seems to complain about "pu" after this series is merged, but I think it's wrong. It says:

  *** CID 1417630:  Memory - illegal accesses  (OVERRUN)
  /notes.c: 458 in load_subtree()
  452     
  453     			/*
  454     			 * Pad the rest of the SHA-1 with zeros,
  455     			 * except for the last byte, where we write
  456     			 * the length:
  457     			 */
  >>>     CID 1417630:  Memory - illegal accesses  (OVERRUN)
  >>>     Overrunning array of 20 bytes at byte offset 20 by dereferencing pointer "&object_oid.hash[len]".
  458     			memset(object_oid.hash + len, 0, GIT_SHA1_RAWSZ - len - 1);
  459     			object_oid.hash[KEY_INDEX] = (unsigned char)len;
  460     
  461     			type = PTR_TYPE_SUBTREE;
  462     		} else {
  463     			/* This can't be part of a note */

I agree that if "len" were 20 here that would be a problem, but I don't think that's possible.

The tool correctly claims that prefix_len can be up to 19, due to the assert:

     3. cond_at_most: Checking prefix_len >= 20UL implies that prefix_len may be up to 19 on the false branch.
  420        if (prefix_len >= GIT_SHA1_RAWSZ)
  421                BUG("prefix_len (%"PRIuMAX") is out of range", (uintmax_t)prefix_len);
Then it claims:
    13. Condition path_len == 2 * (20 - prefix_len), taking false branch.
  430                if (path_len == 2 * (GIT_SHA1_RAWSZ - prefix_len)) {
  431                        /* This is potentially the remainder of the SHA-1 */

So we know that either prefix_len is not 19, or that path_len is not 2 (since that combination would cause us to take the true branch here). But then it goes on to say:

    14. Condition path_len == 2, taking true branch.
  442                } else if (path_len == 2) {
  443                        /* This is potentially an internal node */

which I believe must mean that prefix_len cannot be 19 here. And yet it says:

    15. assignment: Assigning: len = prefix_len. The value of len may now be up to 19.
  444                        size_t len = prefix_len;
  445
  [...]
     17. incr: Incrementing len. The value of len may now be up to 20.
     18. Condition hex_to_bytes(&object_oid.hash[len++], entry.path, 1), taking false branch.
  450                        if (hex_to_bytes(object_oid.hash + len++, entry.path, 1))
  451                                goto handle_non_note; /* entry.path is not a SHA1 */

I think that's impossible, and Coverity simply isn't smart enough to shrink the set of possible values for prefix_len based on the set of if-else conditions.

So nothing to see here, but since I spent 20 minutes scratching my head (and I know others look at Coverity output and may scratch their heads too), I thought it was worth writing up. And also if I'm wrong, it would be good to know. ;)

-Peff
Previous: Johan HerlandNext: Michael Haggerty
Message 19 of 23 in “Clean up notes-related code around `load_subtree()`”
  1. 00/12 Clean up notes-related code around `load_subtree()`Michael Haggerty, Aug 26, 2017
  2. 01/12 notes: make GET_NIBBLE macro more robustMichael Haggerty, Aug 26, 2017
  3. 03/12 load_subtree(): reduce the scope of some local variablesMichael Haggerty, Aug 26, 2017
  4. 04/12 load_subtree(): fix incorrect commentMichael Haggerty, Aug 26, 2017
  5. 06/12 load_subtree(): check earlier whether an internal node is a tree entryMichael Haggerty, Aug 26, 2017
  6. 08/12 get_oid_hex_segment(): return 0 on successMichael Haggerty, Aug 26, 2017
  7. 10/12 get_oid_hex_segment(): don't pad the rest of `oid`Michael Haggerty, Aug 26, 2017
  8. 11/12 hex_to_bytes(): simpler replacement for `get_oid_hex_segment()`Michael Haggerty, Aug 26, 2017
  9. 12/12 load_subtree(): declare some variables to be `size_t`Michael Haggerty, Aug 26, 2017
  10. 07/12 load_subtree(): only consider blobs to be potential notesMichael Haggerty, Aug 26, 2017
  11. 05/12 load_subtree(): separate logic for internal vs. terminal entriesMichael Haggerty, Aug 26, 2017
  12. 09/12 load_subtree(): combine some common codeMichael Haggerty, Aug 26, 2017
  13. 02/12 load_subtree(): remove unnecessary conditionalMichael Haggerty, Aug 26, 2017
  14. Junio C HamanoAug 26, 2017
  15. Michael HaggertyAug 27, 2017
  16. Michael HaggertyAug 28, 2017
  17. Junio C HamanoSep 1, 2017
  18. Johan HerlandAug 26, 2017
  19. Jeff KingSep 9, 2017
  20. Michael HaggertySep 10, 2017
  21. Jeff KingSep 10, 2017
  22. Michael HaggertySep 12, 2017
  23. Lars SchneiderSep 12, 2017

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.