Skip to content

std::vec: Add UB check for set_len, from_raw_parts_in, and etc. #143877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
#![feature(try_trait_v2)]
#![feature(try_with_capacity)]
#![feature(tuple_trait)]
#![feature(ub_checks)]
#![feature(unicode_internals)]
#![feature(unsize)]
#![feature(unwrap_infallible)]
Expand Down
20 changes: 17 additions & 3 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
use core::ptr::{self, NonNull};
use core::slice::{self, SliceIndex};
use core::{fmt, intrinsics};
use core::{fmt, intrinsics, ub_checks};

#[stable(feature = "extract_if", since = "1.87.0")]
pub use self::extract_if::ExtractIf;
Expand Down Expand Up @@ -1058,6 +1058,11 @@ impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[unstable(feature = "allocator_api", issue = "32838")]
pub unsafe fn from_raw_parts_in(ptr: *mut T, length: usize, capacity: usize, alloc: A) -> Self {
ub_checks::assert_unsafe_precondition!(
check_library_ub,
"Vec::from_raw_parts_in requires that length <= capacity",
(length: usize = length, capacity: usize = capacity) => length <= capacity
);
unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } }
}

Expand Down Expand Up @@ -1174,6 +1179,11 @@ impl<T, A: Allocator> Vec<T, A> {
#[unstable(feature = "allocator_api", reason = "new API", issue = "32838")]
// #[unstable(feature = "box_vec_non_null", issue = "130364")]
pub unsafe fn from_parts_in(ptr: NonNull<T>, length: usize, capacity: usize, alloc: A) -> Self {
ub_checks::assert_unsafe_precondition!(
check_library_ub,
"Vec::from_parts_in requires that length <= capacity",
(length: usize = length, capacity: usize = capacity) => length <= capacity
);
unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } }
}

Expand Down Expand Up @@ -1950,7 +1960,11 @@ impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= self.capacity());
ub_checks::assert_unsafe_precondition!(
check_library_ub,
"Vec::set_len requires that new_len <= capacity()",
(new_len: usize = new_len, capacity: usize = self.capacity()) => new_len <= capacity
);

self.len = new_len;
}
Expand Down Expand Up @@ -3695,7 +3709,7 @@ impl<T, A: Allocator> Vec<T, A> {
/// This is optimal if:
///
/// * The tail (elements in the vector after `range`) is empty,
/// * or `replace_with` yields fewer or equal elements than `range`s length
/// * or `replace_with` yields fewer or equal elements than `range`'s length
/// * or the lower bound of its `size_hint()` is exact.
///
/// Otherwise, a temporary vector is allocated and the tail is moved twice.
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/precondition-checks/vec-from-parts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@ run-fail
//@ compile-flags: -Cdebug-assertions=yes
//@ error-pattern: unsafe precondition(s) violated: Vec::from_parts_in requires that length <= capacity
#![feature(allocator_api)]

use std::ptr::NonNull;

fn main() {
let ptr: NonNull<i32> = std::ptr::NonNull::dangling();
// Test Vec::from_parts_in with length > capacity
unsafe {
let alloc = std::alloc::Global;
let _vec = Vec::from_parts_in(ptr, 10, 5, alloc);
}
}
29 changes: 29 additions & 0 deletions tests/ui/precondition-checks/vec-from-raw-parts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//@ run-fail
//@ compile-flags: -Cdebug-assertions=yes
//@ error-pattern: unsafe precondition(s) violated: Vec::from_raw_parts_in requires that length <= capacity
//@ revisions: vec_from_raw_parts vec_from_raw_parts_in string_from_raw_parts

#![feature(allocator_api)]

fn main() {
let ptr = std::ptr::null_mut::<u8>();
// Test Vec::from_raw_parts with length > capacity
unsafe {
#[cfg(vec_from_raw_parts)]
let _vec = Vec::from_raw_parts(ptr, 10, 5);
}

// Test Vec::from_raw_parts_in with length > capacity
unsafe {
let alloc = std::alloc::Global;
#[cfg(vec_from_raw_parts_in)]
let _vec = Vec::from_raw_parts_in(ptr, 10, 5, alloc);
}

// Test String::from_raw_parts with length > capacity
// Because it calls Vec::from_raw_parts, it should also fail
unsafe {
#[cfg(string_from_raw_parts)]
let _vec = String::from_raw_parts(ptr, 10, 5);
}
}
11 changes: 11 additions & 0 deletions tests/ui/precondition-checks/vec-set-len.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//@ run-fail
//@ compile-flags: -Cdebug-assertions=yes
//@ error-pattern: unsafe precondition(s) violated: Vec::set_len requires that new_len <= capacity()

fn main() {
let mut vec: Vec<i32> = Vec::with_capacity(5);
// Test set_len with length > capacity
unsafe {
vec.set_len(10);
}
}