Skip to content

fix(correctness): prevent arithmetic overflow, underflow panics, and … - #120

Closed
IgnatiusPang wants to merge 1 commit into
onecodex:masterfrom
IgnatiusPang:fix/boundary-and-overflow-robustness
Closed

IgnatiusPang wants to merge 1 commit into
onecodex:masterfrom
IgnatiusPang:fix/boundary-and-overflow-robustness

Conversation

@IgnatiusPang

@IgnatiusPang IgnatiusPang commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request: Fix boundary panics, integer overflow/underflow, and slice bounds in bitkmer, kmer, and sequence

Source Branch: fix/boundary-and-overflow-robustness
Target Repository: onecodex/needletail (master)
Commit: f417b8f (fix(correctness): prevent arithmetic overflow, underflow panics, and slice boundary issues)


GitHub Pull Request Template

## Description

This PR resolves arithmetic overflow/underflow conditions, boundary panics, and slice indexing defects across `bitkmer`, `kmer`, and `sequence` in `needletail`:

1. **32-mer Exponentiation Overflow in `bitkmer::extend_kmer`**: `BitKmerSeq::pow(2, u32::from(2 * kmer.1)) - 1` was evaluated on `u64`. For standard 32-mer k-mers (`kmer.1 == 32`), `2 * kmer.1 == 64`, which panics in Rust with `attempt to calculate 2_u64.pow(64_u32), which would overflow` ($2^{64} > \text{u64::MAX}$). Replaced with `BitKmerSeq::MAX` for 32-mers, enabling safe 32-mer analysis.
2. **Underflow and Exponentiation Overflow in `bitkmer::minimizer`**: For 32-mer minimizers, `pow(2, 2 * minmer_size)` overflowed. Additionally, when `minmer_size > kmer.1`, `kmer.1 - minmer_size` underflowed `u8` in debug mode. Guarded with early return and clamped bitmask.
3. **Underflow and Shift Overflow in `bitkmer::bitmer_to_bytes`**: When `kmer.1 == 0`, `offset = (kmer.1 - 1) * 2` and `2 * kmer.1 - 1` underflowed `u8` to 255, resulting in panic on `2u64.pow(255)`. Handled 0-length k-mers returning an empty vector immediately, and guarded `kmer.1 == 32`.
4. **Unsigned Integer Underflow Panic in `kmer::CanonicalKmers`**: When initialized with `k == 0`, `(self.k - 1) as usize` evaluated `0u8 - 1`, panicking in debug mode with `attempt to subtract with overflow`. Added early return in `update_position`.
5. **Algorithmic Scan Stalling / Quadratic Re-scanning on Invalid Bases**: In `bitkmer::update_position` and `CanonicalKmers::update_position`, `kmer_len = 0;` was executed before advancing `start_pos += kmer_len + 1;`, causing `start_pos` to advance by only 1 base instead of advancing past the invalid base. Fixed position advancement order.
6. **Slice Out-of-Bounds Panic on Short Sequences in `sequence::minimizer`**: `&seq[..length]` was evaluated without validating sequence length, panicking with `range end out of range for slice` when `seq.len() < length`. Added early return `Cow::Borrowed(seq)` when `seq.len() < length`.

All changes include regression unit tests and pass all library and integration test suites.

---

### Key Fixes

#### 1. `src/bitkmer.rs`: 32-mer Overflow & Zero-Length Underflow
- Masked bits with `BitKmerSeq::MAX` when `kmer.1 >= 32`.
- Advanced `*start_pos += kmer_len + 1` before resetting `kmer_len = 0`.
- Early return `(0, kmer.1)` in `minimizer` when `minmer_size == 0 || minmer_size > kmer.1`.
- Handled `kmer.1 == 0` in `bitmer_to_bytes`.
- Added tests: `test_32mer_support`, `test_zero_kmer_safety`, `test_minimizer_32mer`.

#### 2. `src/kmer.rs`: Zero-Length Guard in `CanonicalKmers`
- Added `if self.k == 0 { return false; }` in `CanonicalKmers::update_position`.
- Fixed `start_pos` advancement order on invalid characters.
- Added test: `test_canonical_kmers_zero_k`.

#### 3. `src/sequence.rs`: Short Sequence Guard in `minimizer`
- Returned `Cow::Borrowed(seq)` when `length == 0 || seq.len() < length`.
- Updated test: `can_minimize` to verify short and empty sequence inputs.

---

## Validation

- `cargo test --lib`: 45 passed (0 failed)
- `cargo test --tests`: 53 passed (0 failed)
- `cargo fmt --check`: passed cleanly

…slice boundary issues

- bitkmer: guard 32-mer k-mers against 2^64 exponentiation overflow in extend_kmer and minimizer

- bitkmer: advance start_pos past invalid characters before resetting kmer_len in update_position to prevent scan stalling

- bitkmer: handle kmer.1 == 0 safely in bitmer_to_bytes without underflowing to 255

- kmer: guard self.k == 0 in CanonicalKmers::update_position to prevent unsigned underflow panic

- sequence: guard minimizer against sequences shorter than window length to prevent slice out-of-bounds panic

- test: add comprehensive unit tests for 32-mer support and zero boundary safety
@audy

audy commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Thanks. The k=32 overflow is real and I'll fix it separately. Closing because this bundles unrelated changes and conflicts with #121. In the future, please open an issue and disclose AI-generated contributions.

@audy audy closed this Oct 2, 2026
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