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
25 changes: 24 additions & 1 deletion crates/perry-stdlib/src/fetch/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ pub extern "C" fn js_response_body_init_ptr(value: f64) -> i64 {
if let Some(bytes) = unsafe { body_value_buffer_bytes(value) } {
return unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) } as i64;
}
// A Blob / File body contributes its raw bytes. Blob handles are handle-band
// ids (>= FETCH_HANDLE_BAND_START, 0x40000), NOT real pointers, so they must
// be recognized here: otherwise the object-body fallback below runs
// `js_jsvalue_to_string` on the id and dereferences it → SIGSEGV. This is the
// Response-constructor twin of the nested-Blob crash (#6231).
{
let jsval = JSValue::from_bits(value.to_bits());
if jsval.is_pointer() {
let addr = jsval.as_pointer::<u8>() as usize;
if perry_runtime::value::addr_class::is_handle_band(addr) {
if let Some(bytes) = crate::fetch::blob_bytes_clone(addr) {
return unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) }
as i64;
}
}
}
}
// A non-integral or out-of-range value can't be a stream, so skip the
// registry probe for the common cases.
if value.is_finite()
Expand Down Expand Up @@ -97,7 +114,13 @@ pub extern "C" fn js_response_body_init_ptr(value: f64) -> i64 {
// `toString`/`[Symbol.toPrimitive]`) — for a boxed `String` that yields its
// underlying primitive, the result is always a real `StringHeader`.
if JSValue::from_bits(value.to_bits()).is_pointer() {
return js_jsvalue_to_string(value) as i64;
let addr = JSValue::from_bits(value.to_bits()).as_pointer::<u8>() as usize;
// A handle-band id that wasn't a Blob (a Headers/Request/… handle used
// as a body) has no byte payload and is not a real heap object — don't
// deref it via ToString (that was the crash path); fall through instead.
if !perry_runtime::value::addr_class::is_handle_band(addr) {
return js_jsvalue_to_string(value) as i64;
}
}
js_get_string_pointer_unified(value)
}
Expand Down
13 changes: 11 additions & 2 deletions crates/perry-stdlib/src/fetch/request_ctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,17 @@ pub unsafe extern "C" fn js_request_new(
// typed-array/buffer registries first and copy the real bytes verbatim; a
// genuine string body falls through to the lossless StringHeader read so its
// UTF-8 bytes are preserved.
let body: Option<Vec<u8>> = dispatch::body_addr_buffer_bytes(body_ptr as usize)
.or_else(|| dispatch::body_bytes_from_header(body_ptr));
// A Blob / File body is a handle-band id (>= 0x40000), NOT a real
// StringHeader/Buffer pointer, so it must be resolved via the blob registry
// first: reading it through `body_bytes_from_header` would dereference the
// synthetic id → SIGSEGV (#6231, the Request-constructor twin).
let body: Option<Vec<u8>> =
if perry_runtime::value::addr_class::is_handle_band(body_ptr as usize) {
crate::fetch::blob_bytes_clone(body_ptr as usize)
} else {
dispatch::body_addr_buffer_bytes(body_ptr as usize)
.or_else(|| dispatch::body_bytes_from_header(body_ptr))
};
// GET/HEAD requests may not carry a body (WHATWG fetch). Refs #2643.
if body.is_some() && (method == "GET" || method == "HEAD") {
throw_fetch_type_error("Request with GET/HEAD method cannot have body.");
Expand Down
22 changes: 17 additions & 5 deletions crates/perry-stdlib/src/fetch_blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ unsafe fn append_blob_parts(parts: f64, out: &mut Vec<u8>) {
let top16 = bits >> 48;
if top16 == 0x7FFD {
let addr = (bits & 0x0000_FFFF_FFFF_FFFF) as usize;
if addr >= 0x10000 && !perry_runtime::buffer::is_registered_buffer(addr) {
// A real heap pointer (array) lives above the synthetic handle band;
// handle-band ids (blobs/headers/… start at 0x40000) must NOT be read
// as an ArrayHeader or we dereference a fake pointer.
if perry_runtime::value::addr_class::is_above_handle_band(addr)
&& !perry_runtime::buffer::is_registered_buffer(addr)
{
let arr_ptr = addr as *const perry_runtime::array::ArrayHeader;
if !arr_ptr.is_null() {
let gc_header = (arr_ptr as *const u8).sub(perry_runtime::gc::GC_HEADER_SIZE)
Expand Down Expand Up @@ -96,15 +101,22 @@ unsafe fn append_one_blob_part(part: f64, out: &mut Vec<u8>) {
// POINTER_TAG ─ either a Blob handle or a Buffer/Uint8Array.
if top16 == 0x7FFD {
let addr = (bits & 0x0000_FFFF_FFFF_FFFF) as usize;
// Small id → registered Blob handle.
if addr != 0 && addr < 0x10000 {
// Handle-band id (a Blob handle or another synthetic fetch handle) —
// NOT a real pointer. #6231: this previously tested `addr < 0x10000`,
// but blob handle ids start at 0x40000 (FETCH_HANDLE_BAND_START), so a
// `Blob` used as a BlobPart never matched here and fell through to
// `js_jsvalue_to_string`, which dereferenced the id as a pointer and
// SIGSEGV'd. Recognize the whole handle band and copy the blob's bytes.
if perry_runtime::value::addr_class::is_handle_band(addr) {
if let Some(body) = blob_bytes_clone(addr) {
out.extend_from_slice(&body);
return;
}
// A non-blob handle as a BlobPart: skip it rather than deref a
// synthetic id. (Spec would ToString it; skipping is crash-safe.)
return;
}
// BufferHeader (Buffer / Uint8Array / ArrayBuffer)?
if addr >= 0x1000 && perry_runtime::buffer::is_registered_buffer(addr) {
if perry_runtime::buffer::is_registered_buffer(addr) {
let buf = addr as *const perry_runtime::buffer::BufferHeader;
let len = (*buf).length as usize;
let data = perry_runtime::buffer::buffer_data(buf);
Expand Down
Loading