Skip to content

Fix out-of-bounds read in string query suffix check - #4322

Merged
kddnewton merged 1 commit into
ruby:mainfrom
egiurleo:fix-string-query-overread
Oct 30, 2026
Merged

kddnewton merged 1 commit into
ruby:mainfrom
egiurleo:fix-string-query-overread

Conversation

@egiurleo

Copy link
Copy Markdown
Contributor

Motivation

pm_string_query_local, pm_string_query_constant, and pm_string_query_method_name all share pm_slice_type. After it scans the identifier, it checks for a trailing !, ?, or =. When the identifier runs to the end of the slice, that check reads *source with source == end, which is one byte past the input.

Prism::StringQuery never sees this, because Ruby strings always have a readable byte after their contents. Callers that pass a slice of a larger buffer don't have that guarantee, including C callers and the Rust StringQuery API. It shows up in two ways:

  • Wrong answers. StringQuery::new(&b"a!"[..1]).is_local() returns Ok(false), because the byte after the slice happens to be !.
  • Crashes. If the slice ends right before an unmapped page, the read faults.

Since StringQuery is a safe Rust API, this is also a soundness bug. It's present in v2.1.0 and earlier.

Implementation

Check source < end before reading the suffix character.

Tests

Both new tests query a slice whose next byte is !, ?, or =. They fail without the fix and pass with it.

  • test_does_not_read_past_slice in test/prism/ruby/string_query_test.rb calls the C functions directly through FFI. It only runs with the FFI backend, since the C extension always passes whole Ruby strings.
  • subslice_test in rust/ruby-prism/src/string_query.rs covers the Rust StringQuery API, once per query type.

I also checked by hand with a small C program that places a one-byte name right before a guard page. It crashes all three APIs before the fix and returns the correct answers after.

pm_slice_type read *source to check for a trailing !, ?, or = even
when the identifier had already consumed the whole slice, reading
one byte past the input. Check source < end first.

Co-authored-by: Stan Lo <stan.lo@shopify.com>

@kddnewton kddnewton left a comment

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.

Hi @egiurleo!

@kddnewton
kddnewton merged commit c9c1e48 into ruby:main Oct 30, 2026
119 checks passed
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