Skip to content

Create oid from bytes in one unsafe block in gix-hash - #3157

Merged
Sebastian Thiel (Byron) merged 1 commit into
GitoxideLabs:mainfrom
b84330808:gix-hash-single-unsafe-cast
Nov 5, 2026
Merged

Sebastian Thiel (Byron) merged 1 commit into
GitoxideLabs:mainfrom
b84330808:gix-hash-single-unsafe-cast

Conversation

@b84330808

Copy link
Copy Markdown
Contributor

Title: Create oid from bytes in one unsafe block in gix-hash

AI disclosure: Claude Code, an AI agent, wrote this description and the patch, and is speaking
through the b84330808 account. The commit carries a Co-Authored-By trailer for the same reason.

Motivation

This follows #3150. The account holder asked me to look for unsafe in gitoxide's parsers that can
be removed or narrowed, as security hardening and for Google's Patch Rewards program. The cast from
&[u8] to &oid can't be written in safe Rust, but gix-hash had three copies of it, none with a
SAFETY comment. oid::try_from_bytes() gets its input from the tree parser in gix-object, among
others.

Change

  • try_from_bytes() calls from_bytes() after its length check instead of repeating the cast for
    each hash kind. gix-hash now has one unsafe block.
  • Add a SAFETY comment to that block: oid is #[repr(transparent)] over [u8], the cast keeps
    the length, and an unexpected length makes kind() panic but isn't undefined behaviour.
  • Mark from_bytes() #[inline] so callers of the #[inline] try_from_bytes() in other crates
    still see the cast.

Verification (by the agent, on Windows, x86_64-pc-windows-gnu, Rust 1.99.1)

  • cargo test -p gix-hash with --features sha1, sha256 and sha1,sha256: all passed.
  • cargo clippy -p gix-hash --all-targets --features sha1,sha256 --no-deps -- -D warnings: no findings.
  • cargo +stable fmt -p gix-hash -- --check and git diff --check: clean.
  • Miri (nightly 2026-10-23, interpreting aarch64-unknown-linux-gnu, -Zmiri-disable-isolation for
    insta): cargo +nightly miri test --target aarch64-unknown-linux-gnu -p gix-hash --features sha1,sha256 --test hash -- oid:: object_id:: comparisons:: --skip rfc_3174 --skip shambles: 24 passed,
    no undefined behaviour reported. The two skipped tests hash megabytes of input, which is too slow
    under Miri.
  • Release-mode assembly of gix-hash, gix-object and gix-ref (--emit=asm, one codegen unit,
    ignoring symbol hashes, labels and debug directives) is identical before and after.

I used Claude Code to write this change.

`gix-hash` had three copies of the same `unsafe` pointer cast from `&[u8]`
to `&oid`: one in `oid::from_bytes()` and one per hash kind in
`oid::try_from_bytes()`. None had a `SAFETY` comment. `try_from_bytes()` is
used by the tree parser in `gix-object` and by `TryFrom<&[u8]>` for `&oid`
and `ObjectId`, so its input can come straight from untrusted data.

Let `try_from_bytes()` call `from_bytes()` after its length check, and
document why the cast in `from_bytes()` is sound. Now there is a single place
that needs reviewing if `oid` ever changes its representation.

Mark `from_bytes()` `#[inline]` so callers of the `#[inline]`
`try_from_bytes()` in other crates still see the cast. The release-mode
assembly of `gix-hash`, `gix-object` and `gix-ref` is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T16:10:24.044911Z d7949ff PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The byte-to-oid conversion cleanup has no active review issues.

No open issues 🎉

What changed in this PR

This PR continues the unsafe-code hardening work from #3150 by consolidating byte-to-oid conversion in gix-hash without changing length validation.

Changes:

  • Reuse from_bytes() for validated SHA-1 and SHA-256 digests, leaving one unsafe cast.
  • Document the cast’s layout, lifetime, and slice-length safety assumptions.
  • Mark from_bytes() as inline to preserve cross-crate optimization opportunities.
File Description
gix-hash/​src/​oid.rs Centralizes the unsafe conversion, documents its safety, and adds an inline annotation.

Review effort: Balanced

@Byron
Sebastian Thiel (Byron) merged commit 641aca3 into GitoxideLabs:main Nov 5, 2026
52 of 68 checks passed
@Byron

Byron commented Nov 5, 2026

Copy link
Copy Markdown
Member

That's a warning - I rubber-stamped the commit that introduced this, in a rush. While it's not actually unsafe, it's something I wouldn't have allowed either.

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.

3 participants