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
24 changes: 17 additions & 7 deletions crates/perry-runtime/src/buffer/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,28 @@ fn buffer_alloc_small(capacity: u32) -> *mut BufferHeader {
})
}

/// True when `addr` lies inside a small-buffer slab block. Slab allocations
/// carry NO GcHeader, so reading `addr - GC_HEADER_SIZE` there yields the
/// previous allocation's trailing data bytes — a content-dependent fake
/// header. `addr_class::try_read_gc_header` consults this before any deref so
/// brand probes (Temporal/Date/Map/Set) can't misroute a small Buffer whose
/// payload happens to spell a matching `obj_type`.
pub(crate) fn is_small_buf_slab_addr(addr: usize) -> bool {
SMALL_BUF_SLAB.with(|slab_ref| {
slab_ref
.borrow()
.ranges
.iter()
.any(|&(start, end)| addr >= start && addr < end)
})
}

/// Check if a pointer is a registered buffer (for instanceof Uint8Array)
pub fn is_registered_buffer(addr: usize) -> bool {
// Fast path: address falls within a small-buffer slab block. All bytes in
// a slab block belong exclusively to BufferHeader allocations, so any match
// is definitively a buffer pointer.
let in_slab = SMALL_BUF_SLAB.with(|slab_ref| {
let slab = slab_ref.borrow();
slab.ranges
.iter()
.any(|&(start, end)| addr >= start && addr < end)
});
if in_slab {
if is_small_buf_slab_addr(addr) {
return true;
}
// Slow path: large buffers tracked in the HashSet registry.
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod view;
pub use header::{BufferHeader, BUFFER_TYPE_ID, SMALL_BUF_THRESHOLD};

// ---- Re-exports: allocation / registry helpers ----
pub(crate) use header::is_small_buf_slab_addr;
pub use header::{
asymmetric_key_meta, buffer_ab_alias, buffer_alloc, buffer_backing_array_buffer,
buffer_byte_offset, buffer_data, buffer_data_mut, crypto_key_meta, ensure_buffer_ab_alias,
Expand Down
8 changes: 8 additions & 0 deletions crates/perry-runtime/src/value/addr_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ pub(crate) unsafe fn try_read_gc_header(addr: usize) -> Option<&'static GcHeader
if !is_plausible_heap_addr(addr) {
return None;
}
// Small-buffer slab allocations are heap-plausible but carry NO GcHeader —
// `addr - GC_HEADER_SIZE` is the previous slab entry's data bytes, so a
// brand probe (Temporal/Date/Map/Set `obj_type` check) would read a
// content-dependent fake header and misroute (observed: `String(buffer)`
// on a zlib result took the Temporal path and deref'd buffer bytes).
if crate::buffer::is_small_buf_slab_addr(addr) {
return None;
}
Some(&*((addr - GC_HEADER_SIZE) as *const GcHeader))
}

Expand Down