Skip to content

fix: raise RangeError on strings longer than INT_MAX - #762

Merged
flavorjones merged 3 commits into
mainfrom
card-816-sql-query-truncation
Oct 26, 2026
Merged

flavorjones merged 3 commits into
mainfrom
card-816-sql-query-truncation

Conversation

@flavorjones

@flavorjones flavorjones commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Motivation

The C extension passes string lengths to SQLite as (int)RSTRING_LEN(...) when preparing SQL (statement.c#L78), binding String and Blob values (statement.c#L281, #L292, #L303), and returning String and Blob results from custom functions and aggregates (database.c#L532, #L539). On 64-bit platforms a string of 4 GiB or more can have its length wrap to a smaller value, so SQLite reads only a prefix of the string.

Details

Use RSTRING_LENINT, which raises RangeError when the length does not fit in an int.

Converting an aggregate's result had a separate bug: rb_sqlite3_aggregator_final set the result outside rb_protect (aggregator.c#L160-L166), so an exception there escaped SQLite's callback and a later GC crashed the process. Returning a Hash from finalize triggers it today. The first commit sets the result inside the same rb_protect as the call to finalize.

The tests use a string of 2^31 NUL bytes, which CRuby allocates with calloc, so they run fast and use little memory. They skip where long is 32 bits, on TruffleRuby, under valgrind, and on FreeBSD, where I suspect they crashed a CI job.

@flavorjones
flavorjones force-pushed the card-816-sql-query-truncation branch 2 times, most recently from 58ede54 to 72da0ea Compare October 26, 2026 16:59
When a custom aggregate's `finalize` returned a value that could not be
converted to a SQLite result, such as a `Hash`, the exception escaped
SQLite's xFinal callback unprotected, and a later GC could crash the
process.

Set the result inside the same `rb_protect` as the call to `finalize`.
SQL strings, bound String and Blob values, and String and Blob results
of custom functions and aggregates were passed to SQLite with their
length cast to `int`. A string of 4 GiB or more could have its length
wrap to a smaller value, so SQLite read only a prefix and could drop
trailing clauses from a query.

Use `RSTRING_LENINT`, which raises `RangeError` instead.

ref: GHSA-4fqr-96q8-xrfx
@flavorjones
flavorjones force-pushed the card-816-sql-query-truncation branch from 72da0ea to f241dde Compare October 26, 2026 17:29
@flavorjones
flavorjones merged commit 115c997 into main Oct 26, 2026
3 of 4 checks passed
@flavorjones
flavorjones deleted the card-816-sql-query-truncation branch October 26, 2026 17:30
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.

1 participant