about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-15 14:47:10 +0000
committerbors <bors@rust-lang.org>2025-07-15 14:47:10 +0000
commit3014e79f9c8d5510ea7b3a3b70d171d0948b1e96 (patch)
treea98190833007bcd5ad9ad6d25dc4a3853d2ab7a4
parente27f16a499074ba9a87f7f7641d9f64c572863bc (diff)
parenta74a28493a00900616d5e52cd85b8b6bae761935 (diff)
downloadrust-3014e79f9c8d5510ea7b3a3b70d171d0948b1e96.tar.gz
rust-3014e79f9c8d5510ea7b3a3b70d171d0948b1e96.zip
Auto merge of #143877 - xizheyin:143813, r=scottmcm,saethlin
`std::vec`: Add UB check for `set_len`, `from_raw_parts_in`, and etc.

Closes rust-lang/rust#143813

I noticed that `from_parts_in` do the similar things like `from_raw_parts_in`, so I add the UB check in the last commit. If it is not appropriate, I will remove it.

And I fix a typo in the first commit.

r? `@scottmcm`
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/vec/mod.rs20
-rw-r--r--tests/ui/precondition-checks/vec-from-parts.rs15
-rw-r--r--tests/ui/precondition-checks/vec-from-raw-parts.rs29
-rw-r--r--tests/ui/precondition-checks/vec-set-len.rs11
5 files changed, 73 insertions, 3 deletions
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 4ad65e678c3..6b6e4df4cba 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -155,6 +155,7 @@
 #![feature(try_trait_v2)]
 #![feature(try_with_capacity)]
 #![feature(tuple_trait)]
+#![feature(ub_checks)]
 #![feature(unicode_internals)]
 #![feature(unsize)]
 #![feature(unwrap_infallible)]
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 1df6ab672ea..9856e9c18ec 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -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;
@@ -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 } }
     }
 
@@ -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 } }
     }
 
@@ -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;
     }
@@ -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.
diff --git a/tests/ui/precondition-checks/vec-from-parts.rs b/tests/ui/precondition-checks/vec-from-parts.rs
new file mode 100644
index 00000000000..0bafb5aa715
--- /dev/null
+++ b/tests/ui/precondition-checks/vec-from-parts.rs
@@ -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);
+    }
+}
diff --git a/tests/ui/precondition-checks/vec-from-raw-parts.rs b/tests/ui/precondition-checks/vec-from-raw-parts.rs
new file mode 100644
index 00000000000..884d34c0a56
--- /dev/null
+++ b/tests/ui/precondition-checks/vec-from-raw-parts.rs
@@ -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);
+    }
+}
diff --git a/tests/ui/precondition-checks/vec-set-len.rs b/tests/ui/precondition-checks/vec-set-len.rs
new file mode 100644
index 00000000000..0987e7fe028
--- /dev/null
+++ b/tests/ui/precondition-checks/vec-set-len.rs
@@ -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);
+    }
+}