Skip to content

fix(spammer): reject num_generators that would overflow the partition shift#147

Open
Kewe63 wants to merge 1 commit into
circlefin:mainfrom
Kewe63:fix/137-spammer-shift-overflow
Open

fix(spammer): reject num_generators that would overflow the partition shift#147
Kewe63 wants to merge 1 commit into
circlefin:mainfrom
Kewe63:fix/137-spammer-shift-overflow

Conversation

@Kewe63

@Kewe63 Kewe63 commented Jun 14, 2026

Copy link
Copy Markdown

Summary

PartitionMode::Exponential::partition_exponential in crates/spammer/src/accounts.rs performed two unchecked left shifts on num_generators. On a 64-bit target, num_generators >= 65 makes the shift distance reach usize::BITS and the << operation overflows:

  • Debug builds — panics with attempt to shift left with overflow
  • Release builds — shift distance silently wraps; 1usize << 64 evaluates to 1usize << 0 == 1, the smallest-bucket guard sees a non-zero bucket and lets the input through, and the boundary loop emits 64 zero-length ranges plus a final correct range

Verified locally on upstream/main (a85368c) before applying the fix.


Root Cause

if round_div(num_accounts, 1 << (num_generators - 1)) == 0 { ... }

for j in (1..=num_generators - 1).rev() {
    let denom = 1usize << j;
    ...
}

partition_exponential already shifts by num_generators - 1, so the natural upper bound is usize::BITS — a left shift by usize::BITS overflows on every platform, and beyond that the partition concept breaks down (1 << 63 already gives sub-unit smallest buckets for any realistic num_accounts).


Fix

Reject any num_generators > usize::BITS up front with a clear error message, before either shift is computed.


Changes

File: crates/spammer/src/accounts.rs

  • Added early validation rejecting num_generators > usize::BITS with an explicit error.
  • Added test partition_exponential_rejects_shift_overflow covering:
    • Original 65-generator case
    • usize::BITS + 1 boundary
    • usize::MAX extreme

How to Test

# Debug build
cargo test -p spammer --lib
# ✅ 65 passed, 0 failed

# Release build
cargo test -p spammer --lib --release
# ✅ 65 passed, 0 failed

# Format & lint
cargo fmt --check -p spammer
cargo clippy -p spammer --all-targets -- -D warnings
# ✅ Both clean

Pre-existing partition_accounts_linear and partition_accounts_exponential tests are unchanged.


Risk & Impact

Low. Validation is purely additive — valid inputs (num_generators <= usize::BITS) are unaffected. Inputs that previously caused silent UB in release builds or panics in debug builds now return a clear error.

Type: 🐛 Bug fix
Closes: #137

… shift

The exponential account partitioner in the spammer computed a left shift
of num_generators - 1 without first validating the shift distance, in two
places: an entry guard and the boundary-building loop. On a 64-bit
target, asking for 65 or more generators makes the shift distance reach
the platform word width, and the left shift overflows. In debug builds
that panics with "attempt to shift left with overflow"; in release
builds the shift distance silently wraps, the existing smallest-bucket
guard sees a non-zero bucket and lets the input through, and the
boundary loop emits 64 zero-length ranges plus a final correct range.

Add an up-front precondition that num_generators must be at most
usize::BITS, with a clear error message. This is the natural cap: the
boundary loop already computes 1usize << (num_generators - 1), and a
left shift by usize::BITS overflows. Beyond this cap the partition
concept itself breaks down. Add a regression test covering the original
65-generator case, the usize::BITS+1 boundary, and the usize::MAX
extreme.
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.

1 participant