Skip to content

fix(simd_path): clamp resolve_ptrs iteration to buf.len() to prevent OOB panic#652

Closed
elee7420-gif wants to merge 1 commit into
dmtrKovalenko:mainfrom
elee7420-gif:fix/resolve-ptrs-bounds
Closed

fix(simd_path): clamp resolve_ptrs iteration to buf.len() to prevent OOB panic#652
elee7420-gif wants to merge 1 commit into
dmtrKovalenko:mainfrom
elee7420-gif:fix/resolve-ptrs-bounds

Conversation

@elee7420-gif

Copy link
Copy Markdown
Contributor

Problem

ChunkedString::resolve_ptrs() iterates self.indices.len() times over a fixed [*const u8; 32] buffer with no bounds guard. When a file path exceeds 512 bytes (32 chunks × 16 bytes), the loop accesses buf[32] and panics:

index out of bounds: the len is 32 but the index is 32

On macOS PATH_MAX is 1024, so any legitimately long path can trigger this. The panic was found in the field with pi-fff v0.9.6.

Fix

Clamp count to buf.len() so pathological paths are safely truncated instead of crashing. The caller already uses ptrs.len() to control iteration, so a truncated slice is handled correctly downstream.

This is a correctness fix — zero behavioral change for the common case (paths < 512 bytes).

Related

Discovered in the pi-fff integration. See the full diagnosis for details.

resolve_ptrs() iterates self.indices.len() times over a fixed-size
[*const u8; 32] buffer with no guard. When a file path exceeds 512
bytes (32 chunks × 16 bytes), the loop accesses buf[32] and panics:

    index out of bounds: the len is 32 but the index is 32

On macOS PATH_MAX is 1024, so any legitimately long path can trigger
this. Clamp count to buf.len() so pathological paths are truncated
gracefully instead of crashing.

Fixes an OOB panic found in pi-fff v0.9.6.

@dmtrKovalenko dmtrKovalenko left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow thank you for the fix I need to spend some time debugging to understand the origin of the issue. Will merge today anyways

@@ -103,7 +103,7 @@ impl ChunkedString {
arena: ArenaPtr,
buf: &'a mut [*const u8; 32],

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait but this gives us max 32 * 128 bytes = 4096 bytes which is max file size ever available

how is it possible that the indicies array contains more elements

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gustav-fff do you have any ideas?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[triage-bot] DIRECTED:

Buffer holds 32 pointers, each pointing to one SimdChunk. SIMD_CHUNK_BYTES = 16 (crates/fff-core/src/simd_path.rs:7), not 128 — so 32 chunks cover only 32 * 16 = 512 bytes, not 4096.

Path capacity is PATH_BUF_SIZE (crates/fff-core/src/constants.rs:36-39):

  • Linux: libc::PATH_MAX = 4096 → up to 256 chunks
  • macOS: libc::PATH_MAX = 1024 → up to 64 chunks
  • Windows: 4096 → up to 256 chunks

ChunkedString::indices is a SmallVec<[u32; 4]> with no cap, so indices.len() can legitimately reach 64 (macOS) or 256 (Linux/Windows). Any path >512 bytes trips the OOB. The reporter's 500+ byte path on macOS is well within PATH_MAX but exceeds the pointer buffer.

The clamp in this PR is a truncation-based band-aid: paths >512 bytes get scored on their first 512 bytes only, which silently degrades fuzzy match quality for long paths (rare, but real on deeply-nested monorepos).

Proper fix options:

  1. Size the caller's buffer to PATH_BUF_SIZE / SIMD_CHUNK_BYTES (256 on Linux, 64 on macOS) at every call site in score.rs:35,63,69,176,202. Stack cost: 256 * 8 = 2 KiB per scoring frame — acceptable for a scoring hot path that already reserves large buffers.
  2. Keep the 32-ptr buffer but reject/skip paths whose byte_len > 512 at index time so resolve_ptrs never sees them. Cheaper but loses matching on legitimate long paths.

I'd lean (1) — a fixed [*const u8; 256] array in score.rs matches the actual PATH_BUF_SIZE invariant and needs no runtime check. Want me to push that instead of the truncation?

Honk-Honk 🪿

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooops this is a real mistake, we have to have a compile time buffer size per the platform limits not 512 bytes

@dmtrKovalenko

Copy link
Copy Markdown
Owner

I'm closing this PR as it doesn't really fix the issue, have opened a spearate one. (it is preserving your original commit)

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.

3 participants