Skip to content

Assert the nbytes range in sign_extend - #852

Open
thatssoheil wants to merge 1 commit into
tokio-rs:masterfrom
thatssoheil:fix/sign-extend-assertion
Open

thatssoheil wants to merge 1 commit into
tokio-rs:masterfrom
thatssoheil:fix/sign-extend-assertion

Conversation

@thatssoheil

Copy link
Copy Markdown

This adds the assertion #832 asks for, plus the two tests that pin the range it asserts.

sign_extend is private, and all four callers (get_int, get_int_le, try_get_int, try_get_int_le) reach it through get_uint / get_uint_le / try_get_uint / try_get_uint_le, which reject more than 8 bytes via panic_does_not_fit before sign_extend runs. So nbytes > 8 there means a caller broke that contract rather than that a read came up short, and the subtraction is best not done blind:

debug_assert!(nbytes <= 8, "nbytes must be in [0, 8]");

One note on the assertion suggested in the issue itself, debug_assert!(nbytes >= 1 && ...): it cannot be used as written. A zero-length read is a real input here, handled by the existing if nbytes == 0 branch that returns 0 instead of shifting by 64, and asserting nbytes >= 1 panics on it and fails 27 existing tests. This asserts the upper bound only and leaves that branch as it was.

No release-mode behaviour changes: debug_assert is compiled out, and the assert cannot fire on any input the public API previously accepted.

Tests

cargo test passes in debug and release, and cargo fmt --check is clean.

Two tests in a #[cfg(test)] mod tests at the end of the file:

  • sign_extend_asserts_more_than_eight_bytes: #[should_panic(expected = "nbytes must be in [0, 8]")] for sign_extend(0, 9), gated with #[cfg(debug_assertions)] because the assertion is compiled out of a release build.
  • sign_extend_accepts_a_zero_length_read: pins the zero-length read at 0.

Deleting the assertion makes the first fail with "panic did not contain expected string", so both exercise the change.

Disclosure

AI-assisted. I ran the checks above and can explain every line during review.

Fixes #832

sign_extend is private, and every caller reaches it through get_uint /
get_uint_le / try_get_uint / try_get_uint_le, which reject more than 8
bytes before it runs, so `nbytes - 1` underflowing would mean a caller
violated that contract rather than that a read came up short. Assert the
range instead of subtracting blind, which is what tokio-rs#832 asked for.

The assertion suggested in tokio-rs#832, `debug_assert!(nbytes >= 1 && ...)`,
cannot be used as written: it panics on the existing zero-length read
path, which returns 0 rather than shifting by 64, and fails 27 tests.
This asserts the upper bound only and leaves that path as it was.

Tests: sign_extend rejects more than eight bytes (the assertion's own
panic), and a zero-length read still returns 0.

Fixes tokio-rs#832
Copilot AI lite review requested due to automatic review settings September 12, 2026 19:10

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 assertion and tests cover the intended bounds without changing valid behavior.

Pull request overview

Adds a debug-only upper-bound assertion to sign_extend while preserving zero-length reads.

Changes:

  • Assert nbytes <= 8.
  • Add tests for oversized and zero-length inputs.
File summaries
File Description
src/buf/buf_impl.rs Adds the assertion and focused unit tests.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

sign_extend() potential shift underflow for nbytes > 8

2 participants