@@ -8,7 +8,7 @@ use std::{
8
8
} ;
9
9
10
10
#[ cfg( feature = "randomize" ) ]
11
- use rand:: { seq:: SliceRandom , SeedableRng } ;
11
+ use rand:: { seq:: SliceRandom , Rng , SeedableRng } ;
12
12
#[ cfg( feature = "randomize" ) ]
13
13
use rand_xoshiro:: Xoshiro128StarStar ;
14
14
@@ -61,18 +61,30 @@ pub trait LayoutCalculator {
61
61
}
62
62
}
63
63
64
- fn univariant < ' a , V : Idx , F : Deref < Target = & ' a LayoutS < V > > + Debug > (
64
+ fn univariant < ' a , V , F , N > (
65
65
& self ,
66
66
dl : & TargetDataLayout ,
67
67
fields : & [ F ] ,
68
68
repr : & ReprOptions ,
69
69
kind : StructKind ,
70
- ) -> Option < LayoutS < V > > {
70
+ option_niche_guaranteed : N ,
71
+ ) -> Option < LayoutS < V > >
72
+ where
73
+ V : Idx ,
74
+ F : Deref < Target = & ' a LayoutS < V > > + Debug ,
75
+ N : Fn ( & Self ) -> bool + Copy ,
76
+ {
71
77
let pack = repr. pack ;
72
78
let mut align = if pack. is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
73
79
let mut inverse_memory_index: Vec < u32 > = ( 0 ..fields. len ( ) as u32 ) . collect ( ) ;
74
- let optimize = !repr. inhibit_struct_field_reordering_opt ( ) ;
75
- if optimize {
80
+
81
+ // `ReprOptions.layout_seed` is a deterministic seed that we can use to
82
+ // randomize field ordering with
83
+ #[ cfg( feature = "randomize" ) ]
84
+ let mut rng = Xoshiro128StarStar :: seed_from_u64 ( repr. field_shuffle_seed ) ;
85
+
86
+ let can_optimize = !repr. inhibit_struct_field_reordering_opt ( ) ;
87
+ if can_optimize {
76
88
let end =
77
89
if let StructKind :: MaybeUnsized = kind { fields. len ( ) - 1 } else { fields. len ( ) } ;
78
90
let optimizing = & mut inverse_memory_index[ ..end] ;
@@ -94,16 +106,11 @@ pub trait LayoutCalculator {
94
106
// the field ordering to try and catch some code making assumptions about layouts
95
107
// we don't guarantee
96
108
if repr. can_randomize_type_layout ( ) && cfg ! ( feature = "randomize" ) {
109
+ // Shuffle the ordering of the fields
97
110
#[ cfg( feature = "randomize" ) ]
98
- {
99
- // `ReprOptions.layout_seed` is a deterministic seed that we can use to
100
- // randomize field ordering with
101
- let mut rng = Xoshiro128StarStar :: seed_from_u64 ( repr. field_shuffle_seed ) ;
111
+ optimizing. shuffle ( & mut rng) ;
102
112
103
- // Shuffle the ordering of the fields
104
- optimizing. shuffle ( & mut rng) ;
105
- }
106
- // Otherwise we just leave things alone and actually optimize the type's fields
113
+ // Otherwise we just leave things alone and actually optimize the type's fields
107
114
} else {
108
115
match kind {
109
116
StructKind :: AlwaysSized | StructKind :: MaybeUnsized => {
@@ -173,6 +180,32 @@ pub trait LayoutCalculator {
173
180
offset = offset. align_to ( field_align. abi ) ;
174
181
align = align. max ( field_align) ;
175
182
183
+ // If `-Z randomize-layout` is enabled, we pad each field by a multiple of its alignment
184
+ // If layout randomization is disabled, we don't pad it by anything and if it is
185
+ // we multiply the field's alignment by anything from zero to the user provided
186
+ // maximum multiple (defaults to three)
187
+ //
188
+ // When `-Z randomize-layout` is enabled that doesn't necessarily mean we can
189
+ // go ham on every type that at first glance looks valid for layout optimization.
190
+ // `Option` specifically has layout guarantees when it has specific `T` substitutions,
191
+ // such as `Option<NonNull<_>>` or `Option<NonZeroUsize>` both being exactly one `usize`
192
+ // large. As such, we have to ensure that the type doesn't guarantee niche optimization
193
+ // with the current payload
194
+ #[ cfg( feature = "randomize" ) ]
195
+ if repr. can_randomize_type_layout ( ) && !option_niche_guaranteed ( self ) {
196
+ let align_bytes = field_align. abi . bytes ( ) ;
197
+ let random_padding = align_bytes
198
+ . checked_mul ( rng. gen_range ( 0 ..=repr. random_padding_max_factor as u64 ) )
199
+ . unwrap_or ( align_bytes) ;
200
+
201
+ // Attempt to add our extra padding, defaulting to the type's alignment
202
+ if let Some ( randomized_offset) =
203
+ offset. checked_add ( Size :: from_bytes ( random_padding) , dl)
204
+ {
205
+ offset = randomized_offset;
206
+ }
207
+ }
208
+
176
209
debug ! ( "univariant offset: {:?} field: {:#?}" , offset, field) ;
177
210
offsets[ i as usize ] = offset;
178
211
@@ -199,7 +232,7 @@ pub trait LayoutCalculator {
199
232
// Field 5 would be the first element, so memory_index is i:
200
233
// Note: if we didn't optimize, it's already right.
201
234
let memory_index =
202
- if optimize { invert_mapping ( & inverse_memory_index) } else { inverse_memory_index } ;
235
+ if can_optimize { invert_mapping ( & inverse_memory_index) } else { inverse_memory_index } ;
203
236
let size = min_size. align_to ( align. abi ) ;
204
237
let mut abi = Abi :: Aggregate { sized } ;
205
238
// Unpack newtype ABIs and find scalar pairs.
@@ -216,7 +249,7 @@ pub trait LayoutCalculator {
216
249
match field. abi {
217
250
// For plain scalars, or vectors of them, we can't unpack
218
251
// newtypes for `#[repr(C)]`, as that affects C ABIs.
219
- Abi :: Scalar ( _) | Abi :: Vector { .. } if optimize => {
252
+ Abi :: Scalar ( _) | Abi :: Vector { .. } if can_optimize => {
220
253
abi = field. abi ;
221
254
}
222
255
// But scalar pairs are Rust-specific and get
@@ -290,7 +323,7 @@ pub trait LayoutCalculator {
290
323
}
291
324
}
292
325
293
- fn layout_of_struct_or_enum < ' a , V : Idx , F : Deref < Target = & ' a LayoutS < V > > + Debug > (
326
+ fn layout_of_struct_or_enum < ' a , V , F , N > (
294
327
& self ,
295
328
repr : & ReprOptions ,
296
329
variants : & IndexVec < V , Vec < F > > ,
@@ -301,7 +334,13 @@ pub trait LayoutCalculator {
301
334
discriminants : impl Iterator < Item = ( V , i128 ) > ,
302
335
niche_optimize_enum : bool ,
303
336
always_sized : bool ,
304
- ) -> Option < LayoutS < V > > {
337
+ option_niche_guaranteed : N ,
338
+ ) -> Option < LayoutS < V > >
339
+ where
340
+ V : Idx ,
341
+ F : Deref < Target = & ' a LayoutS < V > > + Debug ,
342
+ N : Fn ( & Self ) -> bool + Copy ,
343
+ {
305
344
let dl = self . current_data_layout ( ) ;
306
345
let dl = dl. borrow ( ) ;
307
346
@@ -354,7 +393,7 @@ pub trait LayoutCalculator {
354
393
if !always_sized { StructKind :: MaybeUnsized } else { StructKind :: AlwaysSized }
355
394
} ;
356
395
357
- let mut st = self . univariant ( dl, & variants[ v] , repr, kind) ?;
396
+ let mut st = self . univariant ( dl, & variants[ v] , repr, kind, option_niche_guaranteed ) ?;
358
397
st. variants = Variants :: Single { index : v } ;
359
398
360
399
if is_unsafe_cell {
@@ -457,7 +496,13 @@ pub trait LayoutCalculator {
457
496
let mut variant_layouts = variants
458
497
. iter_enumerated ( )
459
498
. map ( |( j, v) | {
460
- let mut st = self . univariant ( dl, v, repr, StructKind :: AlwaysSized ) ?;
499
+ let mut st = self . univariant (
500
+ dl,
501
+ v,
502
+ repr,
503
+ StructKind :: AlwaysSized ,
504
+ option_niche_guaranteed,
505
+ ) ?;
461
506
st. variants = Variants :: Single { index : j } ;
462
507
463
508
align = align. max ( st. align ) ;
@@ -650,6 +695,7 @@ pub trait LayoutCalculator {
650
695
field_layouts,
651
696
repr,
652
697
StructKind :: Prefixed ( min_ity. size ( ) , prefix_align) ,
698
+ option_niche_guaranteed,
653
699
) ?;
654
700
st. variants = Variants :: Single { index : i } ;
655
701
// Find the first field we can't move later
0 commit comments