Skip to content

Commit d641a1d

Browse files
committed
Remove overalignment from align < 8 allocations
This reorganizes the implementation of the System allocator to permit adding various debuggig features. Currently, all that this implements is a scheme that allocates a little extra space for low-alignment allocations then returns a pointer into the actual allocation which is offset so that it is not over-aligned. This is a huge aid in discovering accidental reliance on over-alignment. Allocators designed for C can be relied upon to produce over-aligned pointers, so alignment-related bugs can be latent for a long time. This implementation is also factored so accommodate other patches to the default allocator which can help in detecting other sources of UB.
1 parent f22819b commit d641a1d

File tree

9 files changed

+152
-16
lines changed

9 files changed

+152
-16
lines changed

library/std/src/alloc.rs

+122
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,128 @@ pub use alloc_crate::alloc::*;
127127
#[derive(Debug, Default, Copy, Clone)]
128128
pub struct System;
129129

130+
use crate::sys::alloc::System as Imp;
131+
132+
// When debug assertions are not enabled, `System` just forwards down to the particular platform
133+
// implementation.
134+
#[cfg(not(debug_assertions))]
135+
#[stable(feature = "alloc_system_type", since = "1.28.0")]
136+
unsafe impl GlobalAlloc for System {
137+
#[inline]
138+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
139+
unsafe { Imp.alloc(layout) }
140+
}
141+
142+
#[inline]
143+
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
144+
unsafe { Imp.alloc_zeroed(layout) }
145+
}
146+
147+
#[inline]
148+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
149+
unsafe { Imp.dealloc(ptr, layout) }
150+
}
151+
152+
#[inline]
153+
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
154+
unsafe { Imp.realloc(ptr, layout, new_size) }
155+
}
156+
}
157+
158+
// Some system allocators (most notably any provided by calling malloc) will always return pointers
159+
// with an alignment of 8. So for any allocation with an alignment less than 8, we increase the
160+
// alignment to 8 and return a pointer which is offset into the allocation such that it is not
161+
// over-aligned.
162+
// We always bump up the size of an allocation by 8 when the alignment is less than 8.
163+
#[cfg(debug_assertions)]
164+
trait LayoutExt {
165+
fn with_alignment_padding(self) -> Self;
166+
unsafe fn add_alignment_padding(self, ptr: *mut u8) -> *mut u8;
167+
unsafe fn remove_alignment_padding(self, ptr: *mut u8) -> *mut u8;
168+
}
169+
#[cfg(debug_assertions)]
170+
impl LayoutExt for Layout {
171+
fn with_alignment_padding(self) -> Self {
172+
if self.align() < 8 {
173+
Layout::from_size_align(self.size() + (8 - self.align()), 8).unwrap()
174+
} else {
175+
self
176+
}
177+
}
178+
179+
unsafe fn add_alignment_padding(self, ptr: *mut u8) -> *mut u8 {
180+
if !ptr.is_null() && self.align() < 8 {
181+
// SAFETY: This must be called on a pointer previously returned by a padded Layout,
182+
// which will always have space to do this offset
183+
unsafe { ptr.add(8 - self.align()) }
184+
} else {
185+
ptr
186+
}
187+
}
188+
189+
unsafe fn remove_alignment_padding(self, ptr: *mut u8) -> *mut u8 {
190+
// We cannot just do the inverse of add_alignment_padding, because if a user deallocates
191+
// with the wrong Layout, we would use that wrong Layout here to deduce the wrong offset to
192+
// remove from the pointer. That would turn code that works fine because the underlying
193+
// allocator ignores the Layout (but is technically UB) into code which does worse UB or
194+
// halts the program with an unhelpful diagnostic from the underlying allocator.
195+
// So we have two reasonable options. We could detect and clearly report the error
196+
// ourselves, or since we know that all our alignment adjustments involve the low 3 bits,
197+
// we could clear those and make this allocator transparent.
198+
// At the moment we do the latter because it is unclear how to emit an error message from
199+
// inside an allocator.
200+
const ALIGNMENT_MASK: usize = usize::MAX << 3;
201+
ptr.map_addr(|addr| addr & ALIGNMENT_MASK)
202+
}
203+
}
204+
205+
// When debug assertions are enabled, we wrap the platform allocator with extra logic to help
206+
// expose bugs.
207+
#[cfg(debug_assertions)]
208+
#[stable(feature = "alloc_system_type", since = "1.28.0")]
209+
unsafe impl GlobalAlloc for System {
210+
#[inline]
211+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
212+
if layout.size() > isize::MAX as usize - 8 {
213+
return ptr::null_mut();
214+
}
215+
unsafe {
216+
let ptr = Imp.alloc(layout.with_alignment_padding());
217+
layout.add_alignment_padding(ptr)
218+
}
219+
}
220+
221+
#[inline]
222+
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
223+
unsafe {
224+
let ptr = Imp.alloc_zeroed(layout.with_alignment_padding());
225+
layout.add_alignment_padding(ptr)
226+
}
227+
}
228+
229+
#[inline]
230+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
231+
unsafe {
232+
let ptr = layout.remove_alignment_padding(ptr);
233+
Imp.dealloc(ptr, layout.with_alignment_padding())
234+
}
235+
}
236+
237+
#[inline]
238+
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
239+
if new_size > isize::MAX as usize - 8 {
240+
return ptr::null_mut();
241+
}
242+
unsafe {
243+
let ptr = layout.remove_alignment_padding(ptr);
244+
let new_layout =
245+
Layout::from_size_align(new_size, layout.align()).unwrap().with_alignment_padding();
246+
let ptr = Imp.realloc(ptr, layout.with_alignment_padding(), new_layout.size());
247+
layout.add_alignment_padding(ptr)
248+
}
249+
}
250+
}
251+
130252
impl System {
131253
#[inline]
132254
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {

library/std/src/sys/common/alloc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::alloc::{GlobalAlloc, Layout, System};
1+
use crate::alloc::{GlobalAlloc, Layout};
22
use crate::cmp;
33
use crate::ptr;
44

@@ -36,7 +36,7 @@ pub const MIN_ALIGN: usize = 16;
3636
pub const MIN_ALIGN: usize = 4;
3737

3838
pub unsafe fn realloc_fallback(
39-
alloc: &System,
39+
alloc: &impl GlobalAlloc,
4040
ptr: *mut u8,
4141
old_layout: Layout,
4242
new_size: usize,

library/std/src/sys/hermit/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use crate::alloc::{GlobalAlloc, Layout, System};
1+
use crate::alloc::{GlobalAlloc, Layout};
22
use crate::ptr;
33
use crate::sys::hermit::abi;
44

5-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
5+
#[derive(Debug, Default, Copy, Clone)]
6+
pub(crate) struct System;
7+
68
unsafe impl GlobalAlloc for System {
79
#[inline]
810
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {

library/std/src/sys/sgx/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::alloc::{GlobalAlloc, Layout, System};
1+
use crate::alloc::{GlobalAlloc, Layout};
22
use crate::ptr;
33
use crate::sys::sgx::abi::mem as sgx_mem;
44
use core::sync::atomic::{AtomicBool, Ordering};
@@ -56,7 +56,9 @@ unsafe impl dlmalloc::Allocator for Sgx {
5656
}
5757
}
5858

59-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
59+
#[derive(Debug, Default, Copy, Clone)]
60+
pub(crate) struct System;
61+
6062
unsafe impl GlobalAlloc for System {
6163
#[inline]
6264
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {

library/std/src/sys/solid/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use crate::{
2-
alloc::{GlobalAlloc, Layout, System},
2+
alloc::{GlobalAlloc, Layout},
33
sys::common::alloc::{realloc_fallback, MIN_ALIGN},
44
};
55

6-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
6+
#[derive(Debug, Default, Copy, Clone)]
7+
pub(crate) struct System;
8+
79
unsafe impl GlobalAlloc for System {
810
#[inline]
911
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {

library/std/src/sys/unix/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use crate::alloc::{GlobalAlloc, Layout, System};
1+
use crate::alloc::{GlobalAlloc, Layout};
22
use crate::ptr;
33
use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};
44

5-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
5+
#[derive(Debug, Default, Copy, Clone)]
6+
pub(crate) struct System;
7+
68
unsafe impl GlobalAlloc for System {
79
#[inline]
810
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {

library/std/src/sys/unsupported/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use crate::alloc::{GlobalAlloc, Layout, System};
1+
use crate::alloc::{GlobalAlloc, Layout};
22
use crate::ptr::null_mut;
33

4-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
4+
#[derive(Debug, Default, Copy, Clone)]
5+
pub(crate) struct System;
6+
57
unsafe impl GlobalAlloc for System {
68
#[inline]
79
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {

library/std/src/sys/wasm/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
//! The crate itself provides a global allocator which on wasm has no
1717
//! synchronization as there are no threads!
1818
19-
use crate::alloc::{GlobalAlloc, Layout, System};
19+
use crate::alloc::{GlobalAlloc, Layout};
2020

2121
static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();
2222

23-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
23+
#[derive(Debug, Default, Copy, Clone)]
24+
pub(crate) struct System;
25+
2426
unsafe impl GlobalAlloc for System {
2527
#[inline]
2628
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {

library/std/src/sys/windows/alloc.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![deny(unsafe_op_in_unsafe_fn)]
22

3-
use crate::alloc::{GlobalAlloc, Layout, System};
3+
use crate::alloc::{GlobalAlloc, Layout};
44
use crate::ffi::c_void;
55
use crate::ptr;
66
use crate::sync::atomic::{AtomicPtr, Ordering};
@@ -187,7 +187,9 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
187187
// the pointer will be aligned to the specified alignment and not point to the start of the allocated block.
188188
// Instead there will be a header readable directly before the returned pointer, containing the actual
189189
// location of the start of the block.
190-
#[stable(feature = "alloc_system_type", since = "1.28.0")]
190+
#[derive(Debug, Default, Copy, Clone)]
191+
pub(crate) struct System;
192+
191193
unsafe impl GlobalAlloc for System {
192194
#[inline]
193195
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {

0 commit comments

Comments
 (0)