@@ -8,7 +8,7 @@ use std::{
88} ;
99
1010#[ cfg( feature = "randomize" ) ]
11- use rand:: { seq:: SliceRandom , SeedableRng } ;
11+ use rand:: { seq:: SliceRandom , Rng , SeedableRng } ;
1212#[ cfg( feature = "randomize" ) ]
1313use rand_xoshiro:: Xoshiro128StarStar ;
1414
@@ -61,18 +61,30 @@ pub trait LayoutCalculator {
6161 }
6262 }
6363
64- fn univariant < ' a , V : Idx , F : Deref < Target = & ' a LayoutS < V > > + Debug > (
64+ fn univariant < ' a , V , F , N > (
6565 & self ,
6666 dl : & TargetDataLayout ,
6767 fields : & [ F ] ,
6868 repr : & ReprOptions ,
6969 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+ {
7177 let pack = repr. pack ;
7278 let mut align = if pack. is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
7379 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 {
7688 let end =
7789 if let StructKind :: MaybeUnsized = kind { fields. len ( ) - 1 } else { fields. len ( ) } ;
7890 let optimizing = & mut inverse_memory_index[ ..end] ;
@@ -94,16 +106,11 @@ pub trait LayoutCalculator {
94106 // the field ordering to try and catch some code making assumptions about layouts
95107 // we don't guarantee
96108 if repr. can_randomize_type_layout ( ) && cfg ! ( feature = "randomize" ) {
109+ // Shuffle the ordering of the fields
97110 #[ 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) ;
102112
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
107114 } else {
108115 match kind {
109116 StructKind :: AlwaysSized | StructKind :: MaybeUnsized => {
@@ -173,6 +180,32 @@ pub trait LayoutCalculator {
173180 offset = offset. align_to ( field_align. abi ) ;
174181 align = align. max ( field_align) ;
175182
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+
176209 debug ! ( "univariant offset: {:?} field: {:#?}" , offset, field) ;
177210 offsets[ i as usize ] = offset;
178211
@@ -199,7 +232,7 @@ pub trait LayoutCalculator {
199232 // Field 5 would be the first element, so memory_index is i:
200233 // Note: if we didn't optimize, it's already right.
201234 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 } ;
203236 let size = min_size. align_to ( align. abi ) ;
204237 let mut abi = Abi :: Aggregate { sized } ;
205238 // Unpack newtype ABIs and find scalar pairs.
@@ -216,7 +249,7 @@ pub trait LayoutCalculator {
216249 match field. abi {
217250 // For plain scalars, or vectors of them, we can't unpack
218251 // newtypes for `#[repr(C)]`, as that affects C ABIs.
219- Abi :: Scalar ( _) | Abi :: Vector { .. } if optimize => {
252+ Abi :: Scalar ( _) | Abi :: Vector { .. } if can_optimize => {
220253 abi = field. abi ;
221254 }
222255 // But scalar pairs are Rust-specific and get
@@ -290,7 +323,7 @@ pub trait LayoutCalculator {
290323 }
291324 }
292325
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 > (
294327 & self ,
295328 repr : & ReprOptions ,
296329 variants : & IndexVec < V , Vec < F > > ,
@@ -301,7 +334,13 @@ pub trait LayoutCalculator {
301334 discriminants : impl Iterator < Item = ( V , i128 ) > ,
302335 niche_optimize_enum : bool ,
303336 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+ {
305344 let dl = self . current_data_layout ( ) ;
306345 let dl = dl. borrow ( ) ;
307346
@@ -354,7 +393,7 @@ pub trait LayoutCalculator {
354393 if !always_sized { StructKind :: MaybeUnsized } else { StructKind :: AlwaysSized }
355394 } ;
356395
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 ) ?;
358397 st. variants = Variants :: Single { index : v } ;
359398
360399 if is_unsafe_cell {
@@ -457,7 +496,13 @@ pub trait LayoutCalculator {
457496 let mut variant_layouts = variants
458497 . iter_enumerated ( )
459498 . 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+ ) ?;
461506 st. variants = Variants :: Single { index : j } ;
462507
463508 align = align. max ( st. align ) ;
@@ -650,6 +695,7 @@ pub trait LayoutCalculator {
650695 field_layouts,
651696 repr,
652697 StructKind :: Prefixed ( min_ity. size ( ) , prefix_align) ,
698+ option_niche_guaranteed,
653699 ) ?;
654700 st. variants = Variants :: Single { index : i } ;
655701 // Find the first field we can't move later
0 commit comments