Skip to content

Pin the reject side of the beacon dirty-bits gate - #140

Merged
thedavidmeister merged 1 commit into
mainfrom
2026-08-19-issue-124-dirty-bits-reject-pins
Aug 19, 2026
Merged

Pin the reject side of the beacon dirty-bits gate#140
thedavidmeister merged 1 commit into
mainfrom
2026-08-19-issue-124-dirty-bits-reject-pins

Conversation

@thedavidmeister

Copy link
Copy Markdown
Contributor

Pins the reject side of the beacon dirty-bits gate
(raw > type(uint160).max in
LibExtrospectERC1967BeaconProxy._tryGetAddress), per the issue's proposed
fix. The suite pinned the accept side (2 ** 160 - 1 via
testMatchesAtMaxAddressBoundary) but nothing pinned 2 ** 160, the first
value the gate must reject, so widening the gate past 160 bits shipped green.

Purely additive:

  • test/concrete/OneAboveMaxAddressBeacon.sol — fixture whose
    implementation() and owner() return exactly 2 ** 160; a widened gate
    truncates it to address(0).
  • test/concrete/DirtyUpperByteBeacon.sol — fixture returning a real address
    with only bits 160-167 set above it; a gate widened to 168 bits or more
    truncates it to the embedded address.
  • One test per fixture per public predicate (isBeaconOwner,
    isBeaconImplementationBytecode), each asserting false against exactly the
    value a truncating gate would report as a match.

No src/ changes. The issue's masking caveat (default-profile kills being
absorbed by the compiler-output pins of #84) no longer applies: those tests
have left this repo, and the widening mutant now survives the default profile
on main outright — verified below.

QA

Discriminating tests:

  • LibExtrospectERC1967BeaconProxyIsBeaconOwnerTest.testReturnsFalseAtOneAboveMaxAddressBoundary
  • LibExtrospectERC1967BeaconProxyIsBeaconOwnerTest.testReturnsFalseOnDirtyUpperByteAboveRealAddress (fuzz)
  • LibExtrospectERC1967BeaconProxyIsBeaconImplementationBytecodeTest.testReturnsFalseAtOneAboveMaxAddressBoundary
  • LibExtrospectERC1967BeaconProxyIsBeaconImplementationBytecodeTest.testReturnsFalseOnDirtyUpperByteAboveRealAddress

Mutations applied (real edits to
src/lib/LibExtrospectERC1967BeaconProxy.sol, default profile, fork file
excluded for missing RPC env):

  1. raw > type(uint160).max -> raw > type(uint168).max (the issue's
    widening mutant). Before this PR's tests: 377 passed, 0 failed —
    SURVIVED
    , identical to the clean baseline. With this PR's tests:
    377 passed, 4 failed — KILLED, the 4 failures being exactly the 4
    discriminating tests above.
  2. if (ok == 0 || raw > type(uint160).max) -> if (ok == 0) (dirty-bits
    clause deleted). With this PR's tests: 374 passed, 7 failed — KILLED: all 4 discriminating tests plus 3 existing dirty-address tests (testReturnsFalseOnInvalidReturnEncoding fuzz in both suites and testFalseOnDirtyAddress in the beaconImplementation suite).

Oracle: the function's own @dev — 32-byte returndata whose upper 12 bytes
are non-zero is one of the folded failure modes, so the gate is a boundary at
2 ** 160 and each side needs a pin. Expected values in the new tests are
chosen adversarially: each equals exactly what the truncating mutant would
report as a match, so a false there separates rejection from truncation.

Category check: additive test-only change; no behavior change, no verdict
change. Clean-source run after adding the tests: 381 passed, 0 failed
(fork file excluded; its 3 tests fail locally only on missing
ARBITRUM_RPC_URL, which is environmental). forge fmt --check clean on all
four touched files.

Closes #124

🤖 Generated with Claude Code

The suite pinned the accept side of `_tryGetAddress`'s dirty-bits
boundary (`2 ** 160 - 1` via testMatchesAtMaxAddressBoundary) but
never the reject side, so widening the gate past 160 bits shipped
green: `raw > type(uint160).max` -> `type(uint168).max` survived the
whole default-profile suite (377 passed, 0 failed, identical to
baseline).

Two additive fixtures pin the reject-side partition:

- OneAboveMaxAddressBeacon returns exactly `2 ** 160`, the first
  value the gate must reject; a widened gate truncates it to
  `address(0)`.
- DirtyUpperByteBeacon returns a real address with only bits 160-167
  set above it; a gate widened to 168 bits or more truncates it to
  the embedded address.

One test per fixture per public predicate (`isBeaconOwner`,
`isBeaconImplementationBytecode`), each asserting false against
exactly the value the truncating mutant would match. All four fail
under the widened gate and pass on main. No src changes.

Closes #124

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thedavidmeister, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7bac4030-1cd0-4be8-bbb5-967558948111

📥 Commits

Reviewing files that changed from the base of the PR and between 37d36d1 and 30148ca.

📒 Files selected for processing (4)
  • test/concrete/DirtyUpperByteBeacon.sol
  • test/concrete/OneAboveMaxAddressBeacon.sol
  • test/src/lib/LibExtrospectERC1967BeaconProxy.isBeaconImplementationBytecode.t.sol
  • test/src/lib/LibExtrospectERC1967BeaconProxy.isBeaconOwner.t.sol

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thedavidmeister
thedavidmeister merged commit 25db368 into main Aug 19, 2026
4 checks passed
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.

The beacon dirty-bits gate has no test on its reject side, so widening it past 160 bits ships green

1 participant