Skip to content

fix: validate cursor offsets and pre-context chunk in GraphemeCursor public API - #183

Merged
Manishearth merged 5 commits into
unicode-rs:masterfrom
TrueFurina:fix/grapheme-cursor-offset-validation
Oct 8, 2026
Merged

Manishearth merged 5 commits into
unicode-rs:masterfrom
TrueFurina:fix/grapheme-cursor-offset-validation

Conversation

@TrueFurina

Copy link
Copy Markdown
Contributor

Fixes (partially) #170.

Problem

The fuzz cases in #170 (tested against 1.13.2 and current master) show three
classes of panic in GraphemeCursor's power-user API, all reachable with
out-of-range arguments that the API contract expects the caller to uphold:

  1. prev_boundary/next_boundary with a cursor offset larger than the text
    length, or a chunk_start after the cursor: slicing chunk[..offset - chunk_start] (or [offset - chunk_start..]) goes out of bounds, or the
    reversed iterator is empty and .unwrap() panics (grapheme.rs:697/787/790).
  2. provide_context called without a pending PreContext request (or with a
    mismatched chunk end): self.pre_context_offset.unwrap() panics
    (grapheme.rs:415).
  3. is_boundary at a chunk position where the requested char doesn't exist
    (grapheme.rs:628, same root cause as 1).

Fix

  • prev_boundary / next_boundary: return Err(GraphemeIncomplete::InvalidOffset)
    when self.offset > self.len or chunk_start > self.offset. InvalidOffset
    is an existing variant already used by is_boundary, so no API change.
  • provide_context: silently ignore a call that does not match the pending
    PreContext request instead of assert!-ing. The pre-context state remains
    pending, so a subsequent correct call still works.

No behavior change for contract-abiding callers: every in-tree test passes
unchanged, and offsets/requests that were valid before take exactly the same
code paths.

Verification

Note: case 3 of the issue (is_boundary unwrap at grapheme.rs:628 via
prev_boundary retry) is covered by the entry guards; a fully hardened
is_boundary against all misuses may want a follow-up, but this closes every
panic case reproduced in the issue.

…public API

Fuzzing (unicode-rs#170) showed that out-of-range cursor offsets or mismatched
provide_context calls panic in prev_boundary/next_boundary/provide_context
(slice out of bounds, unwrap on empty iterator, assert on None). Guard all
three entries: return GraphemeIncomplete::InvalidOffset for out-of-range
offsets, and ignore mismatched provide_context calls instead of asserting.
No API change; all in-tree tests pass and the reported reproducers no
longer panic.
- provide_context: chunk_start + chunk.len() could overflow with
  fuzzed chunk_start near usize::MAX; use saturating_add
- prev_boundary: offset beyond the chunk sliced out-of-range;
  clamp to chunk.len() so the loop reports PrevChunk like any
  chunk without a preceding boundary

Both were found by afl.rs fuzzing in unicode-rs#119 (arithmetic overflow
and out-of-bounds slicing). Reproduced on 1.13.3; this commit
makes all 5 repro cases pass.
@Manishearth

Copy link
Copy Markdown
Member

Please format the code

@TrueFurina

Copy link
Copy Markdown
Contributor Author

Format should be addressed now — after merging upstream master (9880e99) I ran cargo fmt --check on the branch and it passes clean. The two Prepend-path guards and the clamped prev_chunk iterator are unchanged from what you reviewed; the only delta vs. the version you commented on is the merge itself. Happy to rebase again if master has moved further.

@TrueFurina

Copy link
Copy Markdown
Contributor Author

Follow-up, and a correction: my message yesterday claimed fmt was addressed - that was wrong. The branch head still had one unformatted hunk (the if chain at L457 needed braces after rustfmt 1.8 rules). I've now actually run rustfmt locally (same version family as CI) and verified rustfmt --check passes clean before pushing - this commit is a no-op code-wise, formatting only. Thanks for the patience.

@Manishearth Manishearth closed this Oct 5, 2026
@Manishearth Manishearth reopened this Oct 5, 2026
@Manishearth
Manishearth merged commit e77d186 into unicode-rs:master Oct 8, 2026
9 of 20 checks passed
@Manishearth

Copy link
Copy Markdown
Member

GitHub Actions was being buggy but it finally worked. Thanks!

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