Skip to content

AsciiSet::contains() causes out-of-bounds array access for non-ASCII bytes (byte >= 128) #1132

Description

@yanfenghu

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions