@@ -19,23 +19,34 @@ pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<'
19
19
20
20
impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
21
21
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
22
- /// Returns the minimum alignment for the target architecture for allocations of the given size.
23
- fn min_align ( & self , size : u64 , kind : MiriMemoryKind ) -> Align {
22
+ /// Returns the alignment that `malloc` would guarantee for requests of the given size.
23
+ fn malloc_align ( & self , size : u64 ) -> Align {
24
24
let this = self . eval_context_ref ( ) ;
25
- // List taken from `library/std/src/sys/pal/common/alloc.rs`.
26
- // This list should be kept in sync with the one from libstd.
27
- let min_align = match this. tcx . sess . target . arch . as_ref ( ) {
25
+ // The C standard says: "The pointer returned if the allocation succeeds is suitably aligned
26
+ // so that it may be assigned to a pointer to any type of object with a fundamental
27
+ // alignment requirement and size less than or equal to the size requested."
28
+ // So first we need to figure out what the limits are for "fundamental alignment".
29
+ // This is given by `alignof(max_align_t)`. The following list is taken from
30
+ // `library/std/src/sys/pal/common/alloc.rs` (where this is called `MIN_ALIGN`) and should
31
+ // be kept in sync.
32
+ let max_fundamental_align = match this. tcx . sess . target . arch . as_ref ( ) {
28
33
"x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8 ,
29
34
"x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" =>
30
35
16 ,
31
36
arch => bug ! ( "unsupported target architecture for malloc: `{}`" , arch) ,
32
37
} ;
33
- // Windows always aligns, even small allocations.
34
- // Source: <https://support.microsoft.com/en-us/help/286470/how-to-use-pageheap-exe-in-windows-xp-windows-2000-and-windows-server>
35
- // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big.
36
- if kind == MiriMemoryKind :: WinHeap || size >= min_align {
37
- return Align :: from_bytes ( min_align) . unwrap ( ) ;
38
+ // The C standard only requires sufficient alignment for any *type* with size less than or
39
+ // equal to the size requested. Types one can define in standard C seem to never have an alignment
40
+ // bigger than their size. So if the size is 2, then only alignment 2 is guaranteed, even if
41
+ // `max_fundamental_align` is bigger.
42
+ // This matches what some real-world implementations do, see e.g.
43
+ // - https://github.com/jemalloc/jemalloc/issues/1533
44
+ // - https://github.com/llvm/llvm-project/issues/53540
45
+ // - https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm
46
+ if size >= max_fundamental_align {
47
+ return Align :: from_bytes ( max_fundamental_align) . unwrap ( ) ;
38
48
}
49
+ // C doesn't have zero-sized types, so presumably nothing is guaranteed here.
39
50
if size == 0 {
40
51
return Align :: ONE ;
41
52
}
@@ -85,11 +96,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
85
96
& mut self ,
86
97
size : u64 ,
87
98
zero_init : bool ,
88
- kind : MiriMemoryKind ,
89
99
) -> InterpResult < ' tcx , Pointer < Option < Provenance > > > {
90
100
let this = self . eval_context_mut ( ) ;
91
- let align = this. min_align ( size, kind ) ;
92
- let ptr = this. allocate_ptr ( Size :: from_bytes ( size) , align, kind . into ( ) ) ?;
101
+ let align = this. malloc_align ( size) ;
102
+ let ptr = this. allocate_ptr ( Size :: from_bytes ( size) , align, MiriMemoryKind :: C . into ( ) ) ?;
93
103
if zero_init {
94
104
// We just allocated this, the access is definitely in-bounds and fits into our address space.
95
105
this. write_bytes_ptr (
@@ -101,14 +111,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
101
111
Ok ( ptr. into ( ) )
102
112
}
103
113
104
- fn free (
105
- & mut self ,
106
- ptr : Pointer < Option < Provenance > > ,
107
- kind : MiriMemoryKind ,
108
- ) -> InterpResult < ' tcx > {
114
+ fn free ( & mut self , ptr : Pointer < Option < Provenance > > ) -> InterpResult < ' tcx > {
109
115
let this = self . eval_context_mut ( ) ;
110
116
if !this. ptr_is_null ( ptr) ? {
111
- this. deallocate_ptr ( ptr, None , kind . into ( ) ) ?;
117
+ this. deallocate_ptr ( ptr, None , MiriMemoryKind :: C . into ( ) ) ?;
112
118
}
113
119
Ok ( ( ) )
114
120
}
@@ -117,13 +123,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
117
123
& mut self ,
118
124
old_ptr : Pointer < Option < Provenance > > ,
119
125
new_size : u64 ,
120
- kind : MiriMemoryKind ,
121
126
) -> InterpResult < ' tcx , Pointer < Option < Provenance > > > {
122
127
let this = self . eval_context_mut ( ) ;
123
- let new_align = this. min_align ( new_size, kind ) ;
128
+ let new_align = this. malloc_align ( new_size) ;
124
129
if this. ptr_is_null ( old_ptr) ? {
125
130
// Here we must behave like `malloc`.
126
- self . malloc ( new_size, /*zero_init*/ false , kind )
131
+ self . malloc ( new_size, /*zero_init*/ false )
127
132
} else {
128
133
if new_size == 0 {
129
134
// C, in their infinite wisdom, made this UB.
@@ -135,7 +140,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
135
140
None ,
136
141
Size :: from_bytes ( new_size) ,
137
142
new_align,
138
- kind . into ( ) ,
143
+ MiriMemoryKind :: C . into ( ) ,
139
144
) ?;
140
145
Ok ( new_ptr. into ( ) )
141
146
}
0 commit comments