Skip to content

fix: validate object fields before use - #77

Merged
lidel merged 5 commits into
masterfrom
harden-object-parsing
Jul 27, 2026
Merged

fix: validate object fields before use#77
lidel merged 5 commits into
masterfrom
harden-object-parsing

Conversation

@lidel

@lidel lidel commented Jul 26, 2026

Copy link
Copy Markdown
Member

Papercuts: the decoder trusts what an object header says. A blob's declared size is set aside in full before any data arrives. The author and committer parser breaks on an email with two spaces in a row, which git commit-tree writes. A reference that is not 20 bytes is accepted when read, and fails only when the object is written back. Signatures are built by adding to a string in a loop, which is slow for large ones.

Fix

  • Reject a negative blob size, and use only as much memory as the data that arrives.
  • Check the email parts before reading them, so author Name <a b@c> decodes.
  • Check a reference is 20 bytes where it is read.
  • Build signatures with strings.Builder.

Good objects decode as before. Also bumps the version, and fixes a test that failed when git had written a commit-graph.

lidel added 4 commits July 27, 2026 00:54
Several values from an object header were used without checking them
first, so malformed input surfaced later as a confusing failure rather
than a decode error at the point it was read.

- blob: reject a negative declared size, and grow the read buffer from
  the bytes actually received instead of reserving the declared size
  before any body arrives. io.EOF and io.ErrUnexpectedEOF are still
  returned exactly where io.ReadFull returned them.
- personinfo: guard both indexes in the email loop, matching the name
  loop above. An email containing repeated spaces splits into an empty
  part, so "author Name <a  b@c>", which git commit-tree writes, now
  decodes instead of erroring.
- util: shaToCid rejects a reference that is not 20 bytes and stops
  discarding mh.Encode's error. The error propagates through commit,
  tag and tree decode, so a short reference fails while being read
  rather than when the node is encoded again.
decodeGpgSig and readMergeTag appended to a string in a loop, which
reallocates and copies the whole accumulated value on every line. Both
now use a strings.Builder, as the person info email loop already does.

Decoding a commit carrying a 2 MB signature drops from 3.056s to 3ms,
and the cost becomes linear in the signature length rather than
quadratic.
TestObjectParse walks .git/objects and reads each file it finds as a
loose git object. It skipped the pack and info directories by checking
the folder a file sits in directly, so it only caught files one level
down.

Git can also write a commit-graph into a folder inside info. Those
files are a level deeper, so the check missed them, and the test tried
to read a commit-graph as a git object and failed with "zlib: invalid
header". Git writes a split commit-graph on every commit under some
configurations, so the test failed on a clean checkout for anyone with
that turned on.

Skip both directories and everything inside them instead.
@lidel
lidel requested a review from gammazero July 26, 2026 23:03
@lidel
lidel marked this pull request as ready for review July 27, 2026 00:11
Comment thread git_test.go Outdated
Comment thread version.json
WalkDir hands the callback an fs.DirEntry instead of an os.FileInfo, so
it does not stat every entry it visits. All three walks over
.git/objects now use it.

The two benchmarks also skipped pack and info by looking at the folder a
file sits in directly, so a commit-graph one level deeper reached the
parser and failed them with "zlib: invalid header". They now skip both
directories whole, as TestObjectParse does, which also drops a path
split per file.

Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com>

Suggested in #77 (comment)
@github-actions

Copy link
Copy Markdown

Suggested tag: v0.1.3

Comparing to: v0.1.2 (diff)

gorelease says:

# summary
v0.1.3 is a valid semantic version for this release.

gocompat says:

HEAD is now at 76d1dc1 bump version (#76)
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

Cutting a Release (and modifying code files)

This PR is modifying both version.json and code files (not markdown, YAML, TOML or lock files).
The Release Checker is not able to analyse files that are not checked in to master. This might cause the above analysis to be inaccurate.
Please consider performing all the code changes in a separate PR before cutting the release.

Automatically created GitHub Release

A draft GitHub Release has been created.
It is going to be published when this PR is merged.
You can modify its' body to include any release notes you wish to include with the release.

@lidel
lidel merged commit f8d7d2d into master Jul 27, 2026
11 checks passed
lidel added a commit to ipfs/kubo that referenced this pull request Jul 27, 2026
* fix: recover from panics in detached goroutines

ls, dag get and dag export each hand their work to a goroutine and read
the result back over a channel or pipe. Decoding and encoding there runs
whatever codec a block's CID names, so it runs third-party code, and a
panic on a detached goroutine ends the daemon rather than the command.

Each now recovers, logs, and reports through the channel it already
uses. In dag export the recover is registered after the existing cleanup
defer so it runs first, while errCh is still open.

* chore: update boxo, go-ipld-git and go-unixfsnode

Picks up the CAR streaming and object parsing work from ipfs/boxo#1197,
ipfs/go-ipld-git#77 and ipfs/go-unixfsnode#100. All three are pinned to
their branches for now; swap for the tagged releases before merging.

* chore: update boxo, go-ipld-git and go-unixfsnode

go-ipld-git and go-unixfsnode are on their tagged releases. boxo is
pinned to main, which carries ipfs/boxo#1197 but has not been released
yet, so this still needs a boxo release before it can merge.

Notes the CAR truncation marker in the v0.43 changelog, since that is
the user-visible part of the boxo update.

Also stops a slow Ubuntu mirror from failing the ipfs-webui job. That
job installed Playwright OS dependencies for every browser although it
declares no projects and so only ever runs chromium, and the install had
no timeout of its own. When the mirror served 10.7 MB of package indices
at 39 kB/s, apt-get update alone outlasted the job's 20 minute budget and
the run was cancelled before any test started. The install is now scoped
to chromium, capped, and best effort: the runner image already ships what
headless chromium needs, and a library that really is missing surfaces
when the browser fails to launch.
lidel added a commit to ipfs/kubo that referenced this pull request Jul 27, 2026
* fix: recover from panics in detached goroutines

ls, dag get and dag export each hand their work to a goroutine and read
the result back over a channel or pipe. Decoding and encoding there runs
whatever codec a block's CID names, so it runs third-party code, and a
panic on a detached goroutine ends the daemon rather than the command.

Each now recovers, logs, and reports through the channel it already
uses. In dag export the recover is registered after the existing cleanup
defer so it runs first, while errCh is still open.

* chore: update boxo, go-ipld-git and go-unixfsnode

Picks up the CAR streaming and object parsing work from ipfs/boxo#1197,
ipfs/go-ipld-git#77 and ipfs/go-unixfsnode#100. All three are pinned to
their branches for now; swap for the tagged releases before merging.

* chore: update boxo, go-ipld-git and go-unixfsnode

go-ipld-git and go-unixfsnode are on their tagged releases. boxo is
pinned to main, which carries ipfs/boxo#1197 but has not been released
yet, so this still needs a boxo release before it can merge.

Notes the CAR truncation marker in the v0.43 changelog, since that is
the user-visible part of the boxo update.

Also stops a slow Ubuntu mirror from failing the ipfs-webui job. That
job installed Playwright OS dependencies for every browser although it
declares no projects and so only ever runs chromium, and the install had
no timeout of its own. When the mirror served 10.7 MB of package indices
at 39 kB/s, apt-get update alone outlasted the job's 20 minute budget and
the run was cancelled before any test started. The install is now scoped
to chromium, capped, and best effort: the runner image already ships what
headless chromium needs, and a library that really is missing surfaces
when the browser fails to launch.

(cherry picked from commit f9baf8f)
@gammazero
gammazero deleted the harden-object-parsing branch July 28, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants