Skip to content

reftable/record: bound an object record's offset count by its length - #2255

Open
msuozzo wants to merge 1 commit into
gitgitgadget:masterfrom
msuozzo:reftable-obj-record-count
Open

msuozzo wants to merge 1 commit into
gitgitgadget:masterfrom
msuozzo:reftable-obj-record-count

Conversation

@msuozzo

@msuozzo msuozzo commented Oct 13, 2026

Copy link
Copy Markdown

cc: Patrick Steinhardt ps@pks.im

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

Welcome to GitGitGadget

Hi @msuozzo, and welcome to GitGitGadget, the GitHub App to send patch series to the Git mailing list from GitHub Pull Requests.

Please make sure that either:

  • Your Pull Request has a good description, if it consists of multiple commits, as it will be used as cover letter.
  • Your Pull Request description is empty, if it consists of a single commit, as the commit message should be descriptive enough by itself.

You can CC potential reviewers by adding a footer to the PR description with the following syntax:

CC: Revi Ewer <revi.ewer@example.com>, Ill Takalook <ill.takalook@example.net>

NOTE: DO NOT copy/paste your CC list from a previous GGG PR's description,
because it will result in a malformed CC list on the mailing list. See
example.

Also, it is a good idea to review the commit messages one last time, as the Git project expects them in a quite specific form:

  • the lines should not exceed 76 columns,
  • the first line should be like a header and typically start with a prefix like "tests:" or "revisions:" to state which subsystem the change is about, and
  • the commit messages' body should be describing the "why?" of the change.
  • Finally, the commit messages should end in a Signed-off-by: line matching the commits' author.

It is in general a good idea to await the automated test ("Checks") in this Pull Request before contributing the patches, e.g. to avoid trivial issues such as unportable code.

Contributing the patches

Before you can contribute the patches, your GitHub username needs to be added to the list of permitted users. Any already-permitted user can do that, by adding a comment to your PR of the form /allow. A good way to find other contributors is to locate recent pull requests where someone has been /allowed:

Both the person who commented /allow and the PR author are able to /allow you.

An alternative is the channel #git-devel on the Libera Chat IRC network:

<newcontributor> I've just created my first PR, could someone please /allow me? https://github.com/gitgitgadget/git/pull/12345
<veteran> newcontributor: it is done
<newcontributor> thanks!

Once on the list of permitted usernames, you can contribute the patches to the Git mailing list by adding a PR comment /submit.

If you want to see what email(s) would be sent for a /submit request, add a PR comment /preview to have the email(s) sent to you. You must have a public GitHub email address for this. Note that any reviewers CC'd via the list in the PR description will not actually be sent emails.

After you submit, GitGitGadget will respond with another comment that contains the link to the cover letter mail in the Git mailing list archive. Please make sure to monitor the discussion in that thread and to address comments and suggestions (while the comments and suggestions will be mirrored into the PR by GitGitGadget, you will still want to reply via mail).

If you do not want to subscribe to the Git mailing list just to be able to respond to a mail, you can download the mbox from the Git mailing list archive (click the (raw) link), then import it into your mail program. If you use GMail, you can do this via:

curl -g --user "<EMailAddress>:<Password>" \
    --url "imaps://imap.gmail.com/INBOX" -T /path/to/raw.txt

To iterate on your change, i.e. send a revised patch or patch series, you will first want to (force-)push to the same branch. You probably also want to modify your Pull Request description (or title). It is a good idea to summarize the revision by adding something like this to the cover letter (read: by editing the first comment on the PR, i.e. the PR description):

Changes since v1:
- Fixed a typo in the commit message (found by ...)
- Added a code comment to ... as suggested by ...
...

To send a new iteration, just add another PR comment with the contents: /submit.

Need help?

New contributors who want advice are encouraged to join git-mentoring@googlegroups.com, where volunteers who regularly contribute to Git are willing to answer newbie questions, give advice, or otherwise provide mentoring to interested contributors. You must join in order to post or view messages, but anyone can join.

You may also be able to find help in real time in the developer IRC channel, #git-devel on Libera Chat. Remember that IRC does not support offline messaging, so if you send someone a private message and log out, they cannot respond to you. The scrollback of #git-devel is archived, though.

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

Invalid author email in 9b2bc74: "1393068+msuozzo@users.noreply.github.com"

An object record's count of offsets is a 64-bit varint from the table,
and reftable_obj_record_decode() allocates the offsets array from it
through REFTABLE_ALLOC_ARRAY(), which takes a size_t. Where size_t is
32 bits, a count of 2^32 + 1 allocates room for one offset, and the
loop, which runs to the full count, writes an offset from the table
for each varint left in the block past the end of the array. Where
size_t is 64 bits, a large count only makes a large allocation that
the decode then fails to fill.

Nothing in Git reaches this decoder from a repository: a block of a
type other than the one wanted is refused before its records are
decoded, and the object index, the only reader of object blocks, is
used only by the unit tests. The decoder is still wrong.

Each offset takes at least a byte, so refuse a count larger than what
is left of the record.

The Makefile did not build the reftable record unit tests (the Meson
build does). Add the suite there so that the new test runs in both.

Assisted-by: Claude Opus 5.5, Claude Fable 5.1
Signed-off-by: Matthew Suozzo <m@suozzo.co>
@msuozzo
msuozzo force-pushed the reftable-obj-record-count branch from 9b2bc74 to 5fb73ec Compare October 13, 2026 22:28
@dscho

dscho commented Oct 14, 2026

Copy link
Copy Markdown
Member

/allow

@gitgitgadget

gitgitgadget Bot commented Oct 14, 2026

Copy link
Copy Markdown

User msuozzo is now allowed to use GitGitGadget.

WARNING: msuozzo has no public email address set on GitHub; GitGitGadget needs an email address to Cc: you on your contribution, so that you receive any feedback on the Git mailing list. Go to https://github.com/settings/profile to make your preferred email public to let GitGitGadget know which email address to use.

@msuozzo

msuozzo commented Oct 14, 2026

Copy link
Copy Markdown
Author

/preview

@gitgitgadget

gitgitgadget Bot commented Oct 14, 2026

Copy link
Copy Markdown

Preview email sent as pull.2255.git.1791989689429.gitgitgadget@gmail.com

@msuozzo

msuozzo commented Oct 14, 2026

Copy link
Copy Markdown
Author

/submit

@gitgitgadget

gitgitgadget Bot commented Oct 14, 2026

Copy link
Copy Markdown

Submitted as pull.2255.git.1791989883899.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-2255/msuozzo/reftable-obj-record-count-v1

To fetch this version to local tag pr-2255/msuozzo/reftable-obj-record-count-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-2255/msuozzo/reftable-obj-record-count-v1

@gitgitgadget

gitgitgadget Bot commented Oct 19, 2026

Copy link
Copy Markdown

Patrick Steinhardt wrote on the Git mailing list (how to reply to this email):

On Wed, Oct 14, 2026 at 02:58:03PM +0000, Matthew Suozzo via GitGitGadget wrote:
> From: Matthew Suozzo <m@suozzo.co>
> 
> An object record's count of offsets is a 64-bit varint from the table,
> and reftable_obj_record_decode() allocates the offsets array from it
> through REFTABLE_ALLOC_ARRAY(), which takes a size_t. Where size_t is
> 32 bits, a count of 2^32 + 1 allocates room for one offset, and the
> loop, which runs to the full count, writes an offset from the table
> for each varint left in the block past the end of the array. Where
> size_t is 64 bits, a large count only makes a large allocation that
> the decode then fails to fill.
> 
> Nothing in Git reaches this decoder from a repository: a block of a
> type other than the one wanted is refused before its records are
> decoded, and the object index, the only reader of object blocks, is
> used only by the unit tests. The decoder is still wrong.
> 
> Each offset takes at least a byte, so refuse a count larger than what
> is left of the record.

Sorry, but this whole commit message reads very AI-generated to me, as
it is both overly verbose and hard to parse. I would welcome it if you
could maybe touch up the message for normal human beings.

> diff --git a/Makefile b/Makefile
> index a96be506b5..66b7270fc0 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1551,6 +1551,7 @@ CLAR_TEST_SUITES += u-reftable-block
>  CLAR_TEST_SUITES += u-reftable-merged
>  CLAR_TEST_SUITES += u-reftable-pq
>  CLAR_TEST_SUITES += u-reftable-readwrite
> +CLAR_TEST_SUITES += u-reftable-record
>  CLAR_TEST_SUITES += u-reftable-stack
>  CLAR_TEST_SUITES += u-reftable-table
>  CLAR_TEST_SUITES += u-reftable-tree

Makes sense, even though this should probably have been in its own
patch.

> diff --git a/reftable/record.c b/reftable/record.c
> index 1fce441930..96eceddca7 100644
> --- a/reftable/record.c
> +++ b/reftable/record.c
> @@ -589,6 +589,10 @@ static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
>  	if (count == 0)
>  		return start.len - in.len;
>  
> +	/* each offset takes at least a byte */
> +	if (count > in.len)
> +		return REFTABLE_FORMAT_ERROR;

This check doesn't really seem to match what you're describing. What you
describe is an integer overflow, but you only check for a very specific
edge ecase here. I would have expected for example that...

>  	REFTABLE_ALLOC_ARRAY(r->offsets, count);

... this line already detects the overflow and then refuses the
operation. Because `REFTABLE_ALLOC_ARRAY()` itself already tries to
check for overflows.

That being said, if you take a look at `reftable_alloc_size()` you can
see that the input is both of type `size_t`. And _that_ is indeed broken
on platforms where `size_t` is 32 bits, as we'd potentially wrap. But
that fact is not even mentioned as part of the commit message at all.

So overall I'm not convinced that this fix makes sense, we should rather
fix the source of the problem, which is `reftable_alloc_size()`.

Thanks!

Patrick

@gitgitgadget

gitgitgadget Bot commented Oct 19, 2026

Copy link
Copy Markdown

m@suozzo.co wrote on the Git mailing list (how to reply to this email):

October 19, 2026 at 3:46 AM, "Patrick Steinhardt" <ps@pks.im mailto:ps@pks.im?to=%22Patrick%20Steinhardt%22%20%3Cps%40pks.im%3E > wrote:


> 
> On Wed, Oct 14, 2026 at 02:58:03PM +0000, Matthew Suozzo via GitGitGadget wrote:
> 
> > 
> > From: Matthew Suozzo <m@suozzo.co>
> >  
> >  An object record's count of offsets is a 64-bit varint from the table,
> >  and reftable_obj_record_decode() allocates the offsets array from it
> >  through REFTABLE_ALLOC_ARRAY(), which takes a size_t. Where size_t is
> >  32 bits, a count of 2^32 + 1 allocates room for one offset, and the
> >  loop, which runs to the full count, writes an offset from the table
> >  for each varint left in the block past the end of the array. Where
> >  size_t is 64 bits, a large count only makes a large allocation that
> >  the decode then fails to fill.
> >  
> >  Nothing in Git reaches this decoder from a repository: a block of a
> >  type other than the one wanted is refused before its records are
> >  decoded, and the object index, the only reader of object blocks, is
> >  used only by the unit tests. The decoder is still wrong.
> >  
> >  Each offset takes at least a byte, so refuse a count larger than what
> >  is left of the record.
> > 
> Sorry, but this whole commit message reads very AI-generated to me, as
> it is both overly verbose and hard to parse. I would welcome it if you
> could maybe touch up the message for normal human beings.

yeah it was AI generated and i copy-edited it. it didn't feel too excessive since i wanted to be precise about the issue's mechanism, scope (32-bit only), severity (not really reachable), and describe the fix.

any pointers on how you'd reword? remove the exposition?

> 
> > 
> > diff --git a/Makefile b/Makefile
> >  index a96be506b5..66b7270fc0 100644
> >  --- a/Makefile
> >  +++ b/Makefile
> >  @@ -1551,6 +1551,7 @@ CLAR_TEST_SUITES += u-reftable-block
> >  CLAR_TEST_SUITES += u-reftable-merged
> >  CLAR_TEST_SUITES += u-reftable-pq
> >  CLAR_TEST_SUITES += u-reftable-readwrite
> >  +CLAR_TEST_SUITES += u-reftable-record
> >  CLAR_TEST_SUITES += u-reftable-stack
> >  CLAR_TEST_SUITES += u-reftable-table
> >  CLAR_TEST_SUITES += u-reftable-tree
> > 
> Makes sense, even though this should probably have been in its own
> patch.

happy to move it to a separate patch.

> 
> > 
> > diff --git a/reftable/record.c b/reftable/record.c
> >  index 1fce441930..96eceddca7 100644
> >  --- a/reftable/record.c
> >  +++ b/reftable/record.c
> >  @@ -589,6 +589,10 @@ static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
> >  if (count == 0)
> >  return start.len - in.len;
> >  
> >  + /* each offset takes at least a byte */
> >  + if (count > in.len)
> >  + return REFTABLE_FORMAT_ERROR;
> > 
> This check doesn't really seem to match what you're describing. What you
> describe is an integer overflow, but you only check for a very specific
> edge ecase here. I would have expected for example that...
> 
> > 
> > REFTABLE_ALLOC_ARRAY(r->offsets, count);
> > 
> ... this line already detects the overflow and then refuses the
> operation. Because `REFTABLE_ALLOC_ARRAY()` itself already tries to
> check for overflows.
> 
> That being said, if you take a look at `reftable_alloc_size()` you can
> see that the input is both of type `size_t`. And _that_ is indeed broken
> on platforms where `size_t` is 32 bits, as we'd potentially wrap. But
> that fact is not even mentioned as part of the commit message at all.

isn't that "REFTABLE_ALLOC_ARRAY(), which takes a size_t" in the original message?

> 
> So overall I'm not convinced that this fix makes sense, we should rather
> fix the source of the problem, which is `reftable_alloc_size()`.

that felt like a pretty disruptive change i was hesitant to put up as my first patch but i'd be happy to make the change.

> 
> Thanks!
> 
> Patrick
>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants