|
| 1 | +#![deny(unsafe_op_in_unsafe_fn)] |
| 2 | + |
1 | 3 | use crate::alloc::{GlobalAlloc, Layout, System};
|
2 | 4 | use crate::ptr;
|
3 | 5 | use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN};
|
4 | 6 |
|
| 7 | +// SAFETY: All methods implemented follow the contract rules defined |
| 8 | +// in `GlobalAlloc`. |
5 | 9 | #[stable(feature = "alloc_system_type", since = "1.28.0")]
|
6 | 10 | unsafe impl GlobalAlloc for System {
|
7 | 11 | #[inline]
|
8 | 12 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
9 | 13 | if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
|
10 |
| - libc::malloc(layout.size()) as *mut u8 |
| 14 | + // SAFETY: `libc::malloc` is guaranteed to be safe, it will allocate |
| 15 | + // `layout.size()` bytes of memory and return a pointer to it |
| 16 | + unsafe { libc::malloc(layout.size()) as *mut u8 } |
11 | 17 | } else {
|
12 |
| - libc::aligned_alloc(layout.align(), layout.size()) as *mut u8 |
| 18 | + // SAFETY: `libc::aligned_alloc` is guaranteed to be safe if |
| 19 | + // `layout.size()` is a multiple of `layout.align()`. This |
| 20 | + // constraint can be satisfied if `pad_to_align` is called, |
| 21 | + // which creates a layout by rounding the size of this layout up |
| 22 | + // to a multiple of the layout's alignment |
| 23 | + let aligned_layout = layout.pad_to_align(); |
| 24 | + unsafe { libc::aligned_alloc(aligned_layout.align(), aligned_layout.size()) as *mut u8 } |
13 | 25 | }
|
14 | 26 | }
|
15 | 27 |
|
16 | 28 | #[inline]
|
17 | 29 | unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
18 | 30 | if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
|
19 |
| - libc::calloc(layout.size(), 1) as *mut u8 |
| 31 | + // SAFETY: `libc::calloc` is safe as long that `layout.size() * 1` |
| 32 | + // would not result in integer overflow which cannot happen, |
| 33 | + // multiplying by one never overflows |
| 34 | + unsafe { libc::calloc(layout.size(), 1) as *mut u8 } |
20 | 35 | } else {
|
21 |
| - let ptr = self.alloc(layout.clone()); |
| 36 | + // SAFETY: The safety contract for `alloc` must be upheld by the caller |
| 37 | + let ptr = unsafe { self.alloc(layout.clone()) }; |
22 | 38 | if !ptr.is_null() {
|
23 |
| - ptr::write_bytes(ptr, 0, layout.size()); |
| 39 | + // SAFETY: in the case of the `ptr` being not null |
| 40 | + // it will be properly aligned and a valid ptr |
| 41 | + // which satisfies `ptr::write_bytes` safety constrains |
| 42 | + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; |
24 | 43 | }
|
25 | 44 | ptr
|
26 | 45 | }
|
27 | 46 | }
|
28 | 47 |
|
29 | 48 | #[inline]
|
30 | 49 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
31 |
| - libc::free(ptr as *mut libc::c_void) |
| 50 | + // SAFETY: `libc::free` is guaranteed to be safe if `ptr` is allocated |
| 51 | + // by this allocator or if `ptr` is NULL |
| 52 | + unsafe { libc::free(ptr as *mut libc::c_void) } |
32 | 53 | }
|
33 | 54 |
|
34 | 55 | #[inline]
|
35 | 56 | unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
36 | 57 | if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
|
37 |
| - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 |
| 58 | + // SAFETY: `libc::realloc` is safe if `ptr` is allocated by this |
| 59 | + // allocator or NULL |
| 60 | + // - If `new_size` is 0 and `ptr` is not NULL, it will act as `libc::free` |
| 61 | + // - If `new_size` is not 0 and `ptr` is NULL, it will act as `libc::malloc` |
| 62 | + // - Else, it will resize the block accordingly |
| 63 | + unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } |
38 | 64 | } else {
|
39 |
| - realloc_fallback(self, ptr, layout, new_size) |
| 65 | + // SAFETY: The safety contract for `realloc_fallback` must be upheld by the caller |
| 66 | + unsafe { realloc_fallback(self, ptr, layout, new_size) } |
40 | 67 | }
|
41 | 68 | }
|
42 | 69 | }
|
0 commit comments