Skip to content

Commit

Permalink
fix: don't call alloc with a length of 0 (#1027)
Browse files Browse the repository at this point in the history
Ref denoland/deno#27545.

Fixes a leak in deno.serve with an empty response. We would call
`to_string_ptr` with an empty string and then call alloc with a length
of 0 which is UB.

Instead of using `alloc` directly, which has a large number of
invariants to uphold, I've changed to just use `Vec` APIs. That leaves
us with fewer invariants to uphold, handles the tricky cases for us, and
we need to make a `Vec` eventually anyway (since `String` just wraps a
Vec).
  • Loading branch information
nathanwhit authored Jan 4, 2025
1 parent 48492d1 commit 726b802
Showing 1 changed file with 20 additions and 24 deletions.
44 changes: 20 additions & 24 deletions core/runtime/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,16 +243,16 @@ pub fn to_string_ptr(string: &v8::fast_api::FastApiOneByteString) -> String {

// SAFETY: We're allocating a buffer of 2x the input size, writing valid UTF-8, then turning that into a string
unsafe {
// Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
// accidentally creating a slice of u8 which would be invalid.
let layout = std::alloc::Layout::from_size_align(capacity, 1).unwrap();
let out = std::alloc::alloc(layout);
// Create an uninitialized buffer of `capacity` bytes.
let mut buffer = Vec::<u8>::with_capacity(capacity);

let written = latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), out);
let written =
latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), buffer.as_mut_ptr());

debug_assert!(written <= capacity);
buffer.set_len(written);
// We know it's valid UTF-8, so make a string
String::from_raw_parts(out, written, capacity)
String::from_utf8_unchecked(buffer)
}
}

Expand Down Expand Up @@ -312,25 +312,21 @@ pub fn to_cow_one_byte(
return Err("expected one-byte String");
}

// Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
// accidentally creating a slice of u8 which would be invalid.
unsafe {
let layout = std::alloc::Layout::from_size_align(capacity, 1).unwrap();
let out = std::alloc::alloc(layout);

// Write the buffer to a slice made from this uninitialized data
{
let buffer = std::slice::from_raw_parts_mut(out as _, capacity);
string.write_one_byte_uninit(
scope,
buffer,
0,
WriteOptions::NO_NULL_TERMINATION,
);
}
// Create an uninitialized buffer of `capacity` bytes.
let mut buffer = Vec::<u8>::with_capacity(capacity);
// Write the buffer to a slice made from this uninitialized data
string.write_one_byte_uninit(
scope,
buffer.spare_capacity_mut(),
0,
WriteOptions::NO_NULL_TERMINATION,
);

Ok(Vec::from_raw_parts(out, capacity, capacity).into())
}
// SAFETY: We initialized bytes from `0..capacity` in
// `write_one_byte_uninit` above.
unsafe { buffer.set_len(capacity) };

Ok(Cow::Owned(buffer))
}

/// Converts from a raw [`v8::Value`] to the expected V8 data type.
Expand Down

0 comments on commit 726b802

Please sign in to comment.