Skip to content

Commit 726b802

Browse files
authored
fix: don't call alloc with a length of 0 (#1027)
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).
1 parent 48492d1 commit 726b802

File tree

1 file changed

+20
-24
lines changed

1 file changed

+20
-24
lines changed

core/runtime/ops.rs

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -243,16 +243,16 @@ pub fn to_string_ptr(string: &v8::fast_api::FastApiOneByteString) -> String {
243243

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

251-
let written = latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), out);
249+
let written =
250+
latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), buffer.as_mut_ptr());
252251

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

@@ -312,25 +312,21 @@ pub fn to_cow_one_byte(
312312
return Err("expected one-byte String");
313313
}
314314

315-
// Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
316-
// accidentally creating a slice of u8 which would be invalid.
317-
unsafe {
318-
let layout = std::alloc::Layout::from_size_align(capacity, 1).unwrap();
319-
let out = std::alloc::alloc(layout);
320-
321-
// Write the buffer to a slice made from this uninitialized data
322-
{
323-
let buffer = std::slice::from_raw_parts_mut(out as _, capacity);
324-
string.write_one_byte_uninit(
325-
scope,
326-
buffer,
327-
0,
328-
WriteOptions::NO_NULL_TERMINATION,
329-
);
330-
}
315+
// Create an uninitialized buffer of `capacity` bytes.
316+
let mut buffer = Vec::<u8>::with_capacity(capacity);
317+
// Write the buffer to a slice made from this uninitialized data
318+
string.write_one_byte_uninit(
319+
scope,
320+
buffer.spare_capacity_mut(),
321+
0,
322+
WriteOptions::NO_NULL_TERMINATION,
323+
);
331324

332-
Ok(Vec::from_raw_parts(out, capacity, capacity).into())
333-
}
325+
// SAFETY: We initialized bytes from `0..capacity` in
326+
// `write_one_byte_uninit` above.
327+
unsafe { buffer.set_len(capacity) };
328+
329+
Ok(Cow::Owned(buffer))
334330
}
335331

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

0 commit comments

Comments
 (0)