AsciiSet::contains() causes out-of-bounds array access for non-ASCII bytes (byte >= 128)
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
Research direction
Start at src/ascii_set.rs lines 47, 58, and 64, and inspect how AsciiSet::contains, add, and remove calculate mask indexes for bytes at or above 128. Add regression coverage for non-ASCII bytes and verify that the public methods no longer access beyond the four-element mask; run the relevant repository tests.
Written by the indexing model from the issue text.
Description
Hi,
I found a potential memory safety issue in the AsciiSet implementation through Kani formal verification.
Location: src/ascii_set.rs:47, 58, 64
Current Code:
const ASCII_RANGE_LEN: usize = 0x80; // 128
const BITS_PER_CHUNK: usize = 32;
pub struct AsciiSet {
mask: [Chunk; ASCII_RANGE_LEN / BITS_PER_CHUNK], // [u32; 4]
}
impl AsciiSet {
pub(crate) const fn contains(&self, byte: u8) -> bool {
let chunk = self.mask[byte as usize / BITS_PER_CHUNK]; // OOB when byte >= 128!
// …
}
pub const fn add(&self, byte: u8) -> Self {
mask[byte as usize / BITS_PER_CHUNK] |= ... // OOB when byte >= 128!
// ...
}
pub const fn remove(&self, byte: u8) -> Self {
mask[byte as usize / BITS_PER_CHUNK] &= ... // OOB when byte >= 128!
// ...
}
}
Analysis:
Array mask has 4 elements (valid indices: 0-3)
Index calculation: byte as usize / 32
For byte >= 128: index >= 4 (out of bounds)
Current Mitigation:
The documentation mentions this is for ASCII only, and should_percent_encode() checks is_ascii() before calling. However, add() and remove() are public API.
Impact:
Potential out-of-bounds memory access
Undefined behavior
Security concern if untrusted input reaches these methods
Suggested Fix:
pub(crate) const fn contains(&self, byte: u8) -> bool {
if byte >= 0x80 { return false; }
let chunk = self.mask[byte as usize / BITS_PER_CHUNK];
let mask = 1 << (byte as usize % BITS_PER_CHUNK);
(chunk & mask) != 0
}
pub const fn add(&self, byte: u8) -> Self {
let mut mask = self.mask;
if byte < 0x80 {
mask[byte as usize / BITS_PER_CHUNK] |= 1 << (byte as usize % BITS_PER_CHUNK);
}
Self { mask }
}
Could you please confirm if this is a valid security concern?
- Dominant language
- Rust
- Stars
- 1.6k
- Forks
- 406
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from servo/rust-url
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
gitbutlerapp/gitbutler#15998 · 1 comment ·
-
bug triage:deciding
Difficulty 1/5 Under an hour Newbie friendliness 88/100
open-telemetry/otel-arrow#4132 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100