Skip to content

fix(runtime): Buffer.from() handles inline SSO short strings (#1767) - #1773

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-1767-mysql-buffer-segv
May 25, 2026
Merged

proggeramlug merged 1 commit into
mainfrom
worktree-fix-1767-mysql-buffer-segv

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Fixes #1767 — SIGSEGV in js_buffer_from_value reached from @perryts/mysql's executePreparedNow on the first parameterized query.

Root cause

Perry's Small String Optimization stores strings of length 0..=5 inline in the NaN-box payload under SHORT_STRING_TAG (0x7FF9) instead of a heap StringHeader.

js_buffer_from_value gated the string path on is_string(), which is STRING_TAG-only (0x7FFF) and returns false for inline SSO values. So a short-string argument fell through to the pointer-extraction path (bits >> 48 >= 0x7FF8 matches 0x7FF9), where its inline bytes were masked to 48 bits and dereferenced as an ArrayHeader*.

The fault address from the repro was 0x056f676e616d — literally the bytes m a n g o plus the SSO length byte 0x05, i.e. the 5-char apiKey "mango" interpreted as a pointer.

This is a regression: before SSO, "mango" was a heap StringHeader and Buffer.from handled it. It became reachable in @perryts/mysql's prepared-statement param encoder (Buffer.from(v, 'utf8')) once the #1663 microtask fix let execution get that far.

Fix

  • Extract the byte-slice → buffer encoding match from js_buffer_from_string into a shared buffer_from_str_bytes(bytes, encoding) helper.
  • Add an is_short_string() branch to js_buffer_from_value that decodes the inline bytes via short_string_to_buf and routes them through that helper, so heap and inline strings share identical hex/base64/latin1/utf8 semantics.

Testing

  • Two new runtime unit tests (buffer_from_value_decodes_sso_short_string_{utf8,hex}) covering empty + 1..=5-byte SSO values.
  • Full buffer:: test module green (15 tests); cargo fmt --all -- --check clean.
  • End-to-end: compiled the issue's repro4.ts against @perryts/mysql@0.1.4 + a local MySQL. Before the fix it SIGSEGVs on the first POST (backtrace matches the issue exactly); after the fix it survives repeated sequential POSTs, inserts rows, and the 401 / non-SSO-key paths still work — verified with both default (auto-optimize) and --no-auto-optimize builds.

`js_buffer_from_value` only recognized heap strings via the STRING_TAG-only
`is_string()` predicate. Strings of length 0..=5 are stored inline in the
NaN-box payload under SHORT_STRING_TAG (0x7FF9, the Small String
Optimization), which `is_string()` rejects. Such a value fell through to the
pointer-extraction path, where its inline bytes (e.g. the ASCII of a 5-char
`apiKey` like "mango") were masked to 48 bits and dereferenced as an
`ArrayHeader*` — SIGSEGV.

This surfaced as a crash in `js_buffer_from_value` during
`@perryts/mysql`'s prepared-statement param encoder (`Buffer.from(v,
'utf8')`) on the first parameterized query, once the #1663 microtask fix let
execution reach it.

Add an SSO branch that decodes the inline bytes and routes them through a
shared `buffer_from_str_bytes` helper (extracted from `js_buffer_from_string`)
so the heap and inline paths honor identical hex/base64/latin1/utf8 encoding
semantics. Add runtime regression tests covering empty/1..=5-byte SSO values
under utf8 and hex.
@proggeramlug
proggeramlug merged commit 8d76c7c into main May 25, 2026
10 checks passed
@proggeramlug
proggeramlug deleted the worktree-fix-1767-mysql-buffer-segv branch May 25, 2026 10:09
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.

SIGSEGV in js_buffer_from_value via @perryts/mysql executePreparedNow (parameterized queries) — regression at c0cdede0 / 0.5.1028

1 participant