0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-04 01:44:26 -05:00

fix(serde_v8): avoid creating unsound slice reference (#16189)

This commit fixes one ocurrence of unsoundness by using the newly added
API (`v8::String::write_utf8_uninit`).

See also
[`clippy:uninit_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec).

Note that it is not actually a bug. Avoiding unsoundness improves our
code quality.
This commit is contained in:
Nugine 2022-10-08 22:04:00 +08:00 committed by GitHub
parent 206aa19137
commit a3432e54c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -733,28 +733,26 @@ fn to_utf8_fast(
scope: &mut v8::HandleScope, scope: &mut v8::HandleScope,
) -> Option<String> { ) -> Option<String> {
// Over-allocate by 20% to avoid checking string twice // Over-allocate by 20% to avoid checking string twice
let len = s.length(); let str_chars = s.length();
let capacity = (len as f64 * 1.2) as usize; let capacity = (str_chars as f64 * 1.2) as usize;
let mut buf = Vec::with_capacity(capacity); let mut buf = Vec::with_capacity(capacity);
let mut nchars = 0; let mut nchars = 0;
let data = buf.as_mut_ptr(); let bytes_len = s.write_utf8_uninit(
let length = s.write_utf8(
scope, scope,
// SAFETY: we're essentially providing the raw internal slice/buffer owned by the Vec buf.spare_capacity_mut(),
// which fulfills all of from_raw_parts_mut's safety requirements besides "initialization"
// and since we're operating on a [u8] not [T] we can safely assume the slice's values
// are sufficiently "initialized" for writes
unsafe { std::slice::from_raw_parts_mut(data, capacity) },
Some(&mut nchars), Some(&mut nchars),
v8::WriteOptions::NO_NULL_TERMINATION v8::WriteOptions::NO_NULL_TERMINATION
| v8::WriteOptions::REPLACE_INVALID_UTF8, | v8::WriteOptions::REPLACE_INVALID_UTF8,
); );
if nchars < len {
if nchars < str_chars {
return None; return None;
} }
// SAFETY: write_utf8 guarantees `length` bytes are initialized & valid utf8
// SAFETY: write_utf8_uninit guarantees `bytes_len` bytes are initialized & valid utf8
unsafe { unsafe {
buf.set_len(length); buf.set_len(bytes_len);
Some(String::from_utf8_unchecked(buf)) Some(String::from_utf8_unchecked(buf))
} }
} }
@ -765,21 +763,18 @@ fn to_utf8_slow(
) -> String { ) -> String {
let capacity = s.utf8_length(scope); let capacity = s.utf8_length(scope);
let mut buf = Vec::with_capacity(capacity); let mut buf = Vec::with_capacity(capacity);
let data = buf.as_mut_ptr();
let length = s.write_utf8( let bytes_len = s.write_utf8_uninit(
scope, scope,
// SAFETY: we're essentially providing the raw internal slice/buffer owned by the Vec buf.spare_capacity_mut(),
// which fulfills all of from_raw_parts_mut's safety requirements besides "initialization"
// and since we're operating on a [u8] not [T] we can safely assume the slice's values
// are sufficiently "initialized" for writes
unsafe { std::slice::from_raw_parts_mut(data, capacity) },
None, None,
v8::WriteOptions::NO_NULL_TERMINATION v8::WriteOptions::NO_NULL_TERMINATION
| v8::WriteOptions::REPLACE_INVALID_UTF8, | v8::WriteOptions::REPLACE_INVALID_UTF8,
); );
// SAFETY: write_utf8 guarantees `length` bytes are initialized & valid utf8
// SAFETY: write_utf8_uninit guarantees `bytes_len` bytes are initialized & valid utf8
unsafe { unsafe {
buf.set_len(length); buf.set_len(bytes_len);
String::from_utf8_unchecked(buf) String::from_utf8_unchecked(buf)
} }
} }