Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/7473-stringify-key-admission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **`JSON.stringify` no longer segfaults on a key slot holding a NaN-boxed immediate.** `stringify_object_inner` treated "not `STRING_TAG` and not `POINTER_TAG`" as "therefore a raw pointer", so `0x7FFC_0000_0000_0010` was dereferenced as a `StringHeader` and faulted — the same bug class as #7447, now fixed the same way, by deciding on GC allocation membership rather than bit pattern. This is not a mask: the default path's checksum on `json_polyglot/field_access` now matches node exactly (2552985550), where it previously crashed. The crash was **not** introduced by #7432 — it reproduces on older commits once the scavenge cadence stops being degenerate, and #7432 merely stopped masking it. (#7473)
12 changes: 11 additions & 1 deletion crates/perry-runtime/src/json/stringify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1211,8 +1211,18 @@ pub(crate) unsafe fn stringify_object_inner(ptr: *const u8, buf: &mut String, de
let key_tag = key_bits & 0xFFFF_0000_0000_0000;
let key_ptr = if key_tag == STRING_TAG || key_tag == POINTER_TAG {
(key_bits & POINTER_MASK) as *const StringHeader
} else {
} else if ptr_is_tracked_heap_object(key_bits as *const u8) {
// Untagged raw key pointer (#3576 module-slot shape). It must be
// VALIDATED, not assumed: this arm previously accepted anything
// that was not STRING_TAG/POINTER_TAG and dereferenced it, so a
// key slot holding a NaN-boxed immediate — observed as
// `0x7FFC_0000_0000_0010` — was read as a `StringHeader`
// (byte_len at +4, data at +0x14) and SIGSEGV'd. Same bug class as
// #7447, same predicate: decide by GC allocation membership, which
// is dereference-free, rather than by bit pattern.
key_bits as *const StringHeader
} else {
std::ptr::null()
};

// SerializeJSONProperty step 2 (#5909): apply a heap-valued member's
Expand Down
Loading