Skip to content
Closed
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
45 changes: 45 additions & 0 deletions crates/perry-runtime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,51 @@ static KEEP_JS_THROW_ERROR_WITH_CODE: unsafe extern "C" fn(
i32,
) -> ! = js_throw_error_with_code;

/// Build a Node-style system `Error`: `.message` + `.code` (the message→code
/// side table the `.code` getter reads) plus `.syscall` (string) and `.errno`
/// (number) own properties. `perry-ext-http` calls this to surface client
/// transport failures (`ECONNREFUSED`, `ENOTFOUND`, `ECONNRESET`, …) as the
/// real coded `Error` objects Node hands to `request.on('error')`.
///
/// # Safety
/// `msg_ptr`/`code_ptr`/`syscall_ptr` must each point to their stated number of
/// valid bytes, or be null with the matching length `0`.
#[no_mangle]
pub unsafe extern "C" fn js_node_system_error_value(
msg_ptr: *const u8,
msg_len: usize,
code_ptr: *const u8,
code_len: usize,
syscall_ptr: *const u8,
syscall_len: usize,
errno: f64,
) -> f64 {
let err_val = js_error_value_with_code(msg_ptr, msg_len, code_ptr, code_len, 0);
let obj = err_val.to_bits() as *mut crate::object::ObjectHeader;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify direct NaN-box bitcasts to raw pointers in this file.
rg -nP 'to_bits\(\)\s+as\s+\*(mut|const)\s+' crates/perry-runtime/src/error.rs
rg -n 'js_nanbox_get_pointer\(' crates/perry-runtime/src/error.rs

Repository: PerryTS/perry

Length of output: 383


🏁 Script executed:

# Find js_nanbox_get_pointer definition
rg -n 'fn\s+js_nanbox_get_pointer' crates/perry-runtime/

Repository: PerryTS/perry

Length of output: 265


🏁 Script executed:

# Check context around line 375 in error.rs
sed -n '370,380p' crates/perry-runtime/src/error.rs

Repository: PerryTS/perry

Length of output: 642


Decode the NaN-boxed pointer before object mutation.

Line 375 directly bitcasts f64 to *mut ObjectHeader using to_bits(), bypassing NaN-box decoding. This produces an invalid object pointer before js_object_set_field_by_name(...) mutates it. Use js_nanbox_get_pointer instead, which properly decodes the value—the file already uses this pattern at lines 648, 1090, and 1111.

Suggested fix
-    let obj = err_val.to_bits() as *mut crate::object::ObjectHeader;
+    let obj = crate::value::js_nanbox_get_pointer(err_val) as *mut crate::object::ObjectHeader;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let obj = err_val.to_bits() as *mut crate::object::ObjectHeader;
let obj = crate::value::js_nanbox_get_pointer(err_val) as *mut crate::object::ObjectHeader;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/error.rs` at line 375, The variable assignment at
line 375 in the error handling code directly bitcasts the f64 value using
to_bits() without properly decoding the NaN-boxed pointer, which will produce an
invalid object pointer before js_object_set_field_by_name mutates it. Replace
the direct bitcast with a call to js_nanbox_get_pointer to properly decode the
NaN-boxed value first, matching the existing pattern used elsewhere in the
codebase (such as at lines 648, 1090, and 1111).

Source: Coding guidelines

if !syscall_ptr.is_null() && syscall_len > 0 {
let key = js_string_from_bytes(b"syscall".as_ptr(), 7) as *const StringHeader;
let sval_str = js_string_from_bytes(syscall_ptr, syscall_len as u32);
let sval = crate::value::js_nanbox_string(sval_str as i64);
crate::object::js_object_set_field_by_name(obj, key, sval);
}
{
let key = js_string_from_bytes(b"errno".as_ptr(), 5) as *const StringHeader;
crate::object::js_object_set_field_by_name(obj, key, errno);
}
err_val
}

#[used]
static KEEP_JS_NODE_SYSTEM_ERROR_VALUE: unsafe extern "C" fn(
*const u8,
usize,
*const u8,
usize,
*const u8,
usize,
f64,
) -> f64 = js_node_system_error_value;

/// Throw `ERR_PERRY_UNIMPLEMENTED` for a registered-but-stub API. Used
/// by the stub-elimination epic's strict mode (#4918/#4919): a runtime
/// stub calls [`crate::stub_diag::perry_runtime_stub`] to warn, then —
Expand Down