fix(runtime): Buffer write/indexOf accept an SSO string; parameterised pg queries no longer segfault (#11430) - #11468
Conversation
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (4)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughBuffer write methods now handle short-string values through a unified string-pointer accessor. Buffer search methods convert short-string needles using the requested encoding. A regression test covers writes, searches, conversion, byte length, fill, and a length-prefixed string writer. ChangesShort-string Buffer handling
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The reviewed Buffer changes have no established merge-blocking issue; proceed with normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change repairs short-string handling in existing Buffer operations without showing a new external entry point or weakened control. Its effect across all production callers is not fully established. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Fixes #11430
What was wrong
On current main, every parameterised node-postgres query segfaulted on the main thread in
js_buffer_write_len. The cause is an interaction between two changes, not the Buffer.write argument handling itself:String(n),`${n}`andn.toString()return small values as SSO. An SSO value is a short string stored inline in the NaN-box, with no heapStringHeaderbehind it.Buffer.prototype.writeand the<encoding>Writefamily (utf8Write,latin1Write, …) inobject/buffer_dispatch.rsaccept SSO throughis_buffer_dispatch_string. They then masked the value's low 48 bits into a*const StringHeader, which turns the inline characters into an address, andjs_buffer_write_lendereferenced it.node-postgres binds every parameter as
writer.addInt32PrefixedString(String(value))→buffer.write(value, offset, 'utf-8'), soclient.query("… $1", [7])crashed. This is the backtrace from the issue:Package-free reduction:
Buffer.alloc(8).write(String(7), 1, "utf-8"). It segfaults on main, and Node prints1.The same family had a quieter sibling.
indexOf/lastIndexOf/includeswith an SSO needle masked it the same way. The value was then not recognised as a string, so the call returned-1instead of the index (for example,Buffer.from("xx7yy").indexOf(String(7))returned -1; Node returns 2).The fix
buffer_dispatch.rs: thewriteand<encoding>Writearms get the string through a newbuffer_dispatch_string_ptr, which callsjs_get_string_pointer_unified. That function materializes an SSO value onto the heap and returns a heap string's header unchanged.buffer/cmp.rs: the needle resolvers forindexOf,lastIndexOfandincludeshandle an SSO needle first, through the same materialization and the same per-encoding decode.Evidence
Arm A is main at 2c90f29 and arm B is this branch. Both were built the same way (
cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static, codegen-units 16,PERRY_NO_AUTO_OPTIMIZE=1) on Linux x86_64. The database was a live PostgreSQL 16.15 with pg 8.23.0 compiled from source. The oracle is Node 26.5.1.test_gap_11430_buffer_write_short_stringSELECT $1::int * 3after a parameterless queryPoolwith max 3, pool-client transaction), scram-sha-256 userscripts/turnloop/apps/pg_parity.ts(parameters only via SQL text)The gap test covers:
writewith and without offset, length and encoding.utf8Write,latin1WriteandhexWrite.indexOfandlastIndexOfwith and without an encoding, andincludes.String(n),(-42).toString()and a template string.Writer.addInt32PrefixedStringshape, checked byte for byte.The same probe with its
worker_threadssection still fails on this branch. That is #11340, fixed separately by #11444.Gap subset through
run_parity_tests.sh(66 tests matching buffer / string / write / indexOf): A → B shows 63 PASS → PASS and 1 CRASH → PASS (the new test). Two tests each reportedNORESULT(a harness stall at host load 50+) in one arm. Both passed when re-run on B. There are no regressions. Each arm ran against its own workspace clone at its own commit.Checks run
RUST_TEST_THREADS=1 cargo test --release -p perry-runtime --lib buffer: 178 passed, 0 failed.RUSTFLAGS=-D warnings cargo check -p perry-runtime --all-targets: clean.cargo fmt --check: clean.SKIP_COMPILE_GATES=1 scripts/run_lint_gates.sh: 90 of 92 script gates pass. The two failures are Public benchmark evidence freshness (red on main) andcargo xwin(not installed on the host). The compile tier was not run. The string-payload ratchet first flagged an open-codedStringHeaderoffset in the new SSO helper; it now reads throughOwnedStringBytes, and the gate passes.Not run
Noticed, not changed
A few option-object readers in the same area only accept a heap string (the
== 0x7FFFchecks inbuffer_dispatch.rs's key-exportformatandhasOwnPropertykey handling, andu8_codec.rs). An SSO value there is treated as "not a string", which gives a wrong answer rather than a crash. ThehasOwnPropertycases in the gap probe already match Node. These are not touched here.Summary by CodeRabbit
Buffer.writeand encoding-specific writes for short strings, preventing crashes in parameterized database queries.indexOf,lastIndexOf, andincludeswhen searching for short strings.