@@ -2,7 +2,8 @@ use std::cmp;
22use std:: ops:: Range ;
33
44use rustc_abi:: {
5- Align , ArmCall , BackendRepr , CanonAbi , ExternAbi , HasDataLayout , Reg , Size , WrappingRange ,
5+ Align , ArmCall , BackendRepr , CanonAbi , ExternAbi , FieldsShape , HasDataLayout , Reg , Size ,
6+ VariantIdx , Variants , WrappingRange ,
67} ;
78use rustc_ast as ast;
89use rustc_ast:: { InlineAsmOptions , InlineAsmTemplatePiece } ;
@@ -11,7 +12,7 @@ use rustc_hir::attrs::AttributeKind;
1112use rustc_hir:: lang_items:: LangItem ;
1213use rustc_lint_defs:: builtin:: TAIL_CALL_TRACK_CALLER ;
1314use rustc_middle:: mir:: { self , AssertKind , InlineAsmMacro , SwitchTargets , UnwindTerminateReason } ;
14- use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf , ValidityRequirement } ;
15+ use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf , TyAndLayout , ValidityRequirement } ;
1516use rustc_middle:: ty:: print:: { with_no_trimmed_paths, with_no_visible_paths} ;
1617use rustc_middle:: ty:: { self , Instance , Ty , TypeVisitableExt } ;
1718use rustc_middle:: { bug, span_bug} ;
@@ -618,15 +619,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
618619
619620 if self . fn_abi . conv == CanonAbi :: Arm ( ArmCall :: CCmseNonSecureEntry ) {
620621 // The return value of an `extern "cmse-nonsecure-entry"` function crosses the
621- // secure boundary. Zero padding bytes so information does not leak.
622- //
623- // This only zeroes "guaranteed" padding. There may be more bytes that are
624- // padding for some but not all variants of this type; those are not zeroed.
625- //
626- // Returning a value with value-dependent padding will instead trigger a lint.
622+ // secure boundary. Clear any padding bytes so information does not leak.
627623 let ret_layout = self . fn_abi . ret . layout ;
628- let uninit_ranges = ret_layout. padding_ranges ( bx. cx ( ) ) ;
629- self . zero_byte_ranges ( bx, llslot, ret_layout. size , & uninit_ranges) ;
624+ self . clear_padding_cmse ( bx, llslot, ret_layout. size , ret_layout) ;
630625 }
631626
632627 load_cast ( bx, cast_ty, llslot, self . fn_abi . ret . layout . align . abi )
@@ -1745,22 +1740,166 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
17451740 }
17461741 }
17471742
1743+ /// When using CMSE, values that cross the secure boundary from secure to non-secure mode can
1744+ /// contain stale secure data in their padding bytes. This function clears that data. This is
1745+ /// required when a value is:
1746+ ///
1747+ /// - passed to an `extern "cmse-nonsecure-call"` function
1748+ /// - returned from an `extern "cmse-nonsecure-entry"` function
1749+ ///
1750+ /// This function clears both:
1751+ ///
1752+ /// - variant-independent padding, bytes that are padding for all valid values of the type
1753+ /// - variant-dependent padding, bytes that are padding for some but not all values of the type
1754+ ///
1755+ /// Clearing variant-dependent padding requires looking at the data at runtime to determine what
1756+ /// bytes to clear.
1757+ fn clear_padding_cmse (
1758+ & mut self ,
1759+ bx : & mut Bx ,
1760+ base_ptr : Bx :: Value ,
1761+ limit : Size ,
1762+ layout : TyAndLayout < ' tcx > ,
1763+ ) {
1764+ // First clear variant-independent padding, a series of memsets.
1765+ let variant_independent = layout. variant_independent_padding_ranges ( self . cx ) ;
1766+ self . zero_byte_ranges ( bx, base_ptr, Size :: ZERO , limit, & variant_independent) ;
1767+
1768+ // Then clear the extra padding of the active variant of any (nested) enum.
1769+ self . clear_variant_dependent_padding ( bx, base_ptr, Size :: ZERO , limit, layout) ;
1770+ }
1771+
1772+ fn clear_variant_dependent_padding (
1773+ & mut self ,
1774+ bx : & mut Bx ,
1775+ base_ptr : Bx :: Value ,
1776+ base_offset : Size ,
1777+ limit : Size ,
1778+ layout : TyAndLayout < ' tcx > ,
1779+ ) {
1780+ let cx = self . cx ;
1781+
1782+ if !layout. has_variant_dependent_padding ( cx) {
1783+ return ;
1784+ }
1785+
1786+ // Recurse into aggregate fields/elements to reach any nested enums.
1787+ match layout. fields {
1788+ FieldsShape :: Array { stride, count } => {
1789+ let elem = layout. field ( cx, 0 ) ;
1790+ if elem. has_variant_dependent_padding ( cx) {
1791+ for idx in 0 ..count {
1792+ let off = base_offset + idx * stride;
1793+ self . clear_variant_dependent_padding ( bx, base_ptr, off, limit, elem) ;
1794+ }
1795+ }
1796+ }
1797+ FieldsShape :: Arbitrary { .. } => {
1798+ for i in 0 ..layout. fields . count ( ) {
1799+ let field = layout. field ( cx, i) ;
1800+ if field. has_variant_dependent_padding ( cx) {
1801+ let off = base_offset + layout. fields . offset ( i) ;
1802+ self . clear_variant_dependent_padding ( bx, base_ptr, off, limit, field) ;
1803+ }
1804+ }
1805+ }
1806+ FieldsShape :: Primitive | FieldsShape :: Union ( _) => { /* nothing to visit */ }
1807+ }
1808+
1809+ // If this is not a multi-variant enum, we're done.
1810+ let Variants :: Multiple { ref variants, .. } = layout. variants else {
1811+ return ;
1812+ } ;
1813+
1814+ // Collect variants that will need padding cleared.
1815+ let mut work = Vec :: with_capacity ( variants. len ( ) ) ;
1816+ for i in 0 ..variants. len ( ) {
1817+ let idx = VariantIdx :: from_usize ( i) ;
1818+ let variant = layout. for_variant ( cx, idx) ;
1819+
1820+ // Don't consider uninhabited variants.
1821+ if variant. is_uninhabited ( ) {
1822+ continue ;
1823+ }
1824+
1825+ let variant_dependent = layout. variant_dependent_padding_ranges ( cx, idx) ;
1826+ let has_nested_variant_dependent = ( 0 ..variant. fields . count ( ) )
1827+ . any ( |i| variant. field ( cx, i) . has_variant_dependent_padding ( cx) ) ;
1828+
1829+ if !variant_dependent. is_empty ( ) || has_nested_variant_dependent {
1830+ work. push ( ( idx, variant, variant_dependent) ) ;
1831+ }
1832+ }
1833+
1834+ if work. is_empty ( ) {
1835+ return ;
1836+ }
1837+
1838+ // Build the switch and clear the appropriate padding for each variant.
1839+ let root_block = bx. llbb ( ) ;
1840+ let join_block = bx. append_sibling_block ( "cmse_pad_join" ) ;
1841+ let mut cases = Vec :: with_capacity ( work. len ( ) ) ;
1842+
1843+ for ( idx, variant, variant_dependent) in work. into_iter ( ) {
1844+ let Some ( discr) = layout. ty . discriminant_for_variant ( bx. tcx ( ) , idx) else {
1845+ bug ! ( "multi-variant layout on a type without discriminants" ) ;
1846+ } ;
1847+
1848+ let variant_block = bx. append_sibling_block ( "cmse_pad_variant" ) ;
1849+ bx. switch_to_block ( variant_block) ;
1850+
1851+ // Clear the padding of this variant.
1852+ self . zero_byte_ranges ( bx, base_ptr, base_offset, limit, & variant_dependent) ;
1853+
1854+ // Recurse into the fields.
1855+ for i in 0 ..variant. fields . count ( ) {
1856+ let field = variant. field ( cx, i) ;
1857+ let off = base_offset + variant. fields . offset ( i) ;
1858+ self . clear_variant_dependent_padding ( bx, base_ptr, off, limit, field) ;
1859+ }
1860+
1861+ bx. br ( join_block) ;
1862+ cases. push ( ( discr. val , variant_block) ) ;
1863+ }
1864+
1865+ // Construct the dispatch.
1866+ bx. switch_to_block ( root_block) ;
1867+
1868+ let discr_ty = layout. ty . discriminant_ty ( bx. tcx ( ) ) ;
1869+ let enum_ptr = bx. inbounds_ptradd ( base_ptr, bx. const_usize ( base_offset. bytes ( ) ) ) ;
1870+ let operand = OperandRef {
1871+ val : OperandValue :: Ref ( PlaceValue :: new_sized ( enum_ptr, layout. align . abi ) ) ,
1872+ layout,
1873+ move_annotation : None ,
1874+ } ;
1875+ let discr = operand. codegen_get_discr ( self , bx, discr_ty) ;
1876+
1877+ // Default to the join block (for variants without variant-dependent padding).
1878+ bx. switch ( discr, join_block, cases. into_iter ( ) ) ;
1879+
1880+ bx. switch_to_block ( join_block) ;
1881+ }
1882+
17481883 fn zero_byte_ranges (
17491884 & mut self ,
17501885 bx : & mut Bx ,
17511886 ptr : Bx :: Value ,
1887+ offset : Size ,
17521888 limit : Size ,
17531889 ranges : & [ Range < Size > ] ,
17541890 ) {
17551891 let zero = bx. const_u8 ( 0 ) ;
17561892
17571893 for range in ranges {
1758- let end = cmp:: min ( range. end , limit) ;
1894+ let start = range. start + offset;
1895+ let end = range. end + offset;
1896+
1897+ let end = cmp:: min ( end, limit) ;
17591898 if range. start >= end {
17601899 continue ;
17611900 }
1762- let offset = bx. const_usize ( range . start . bytes ( ) ) ;
1763- let len = bx. const_usize ( ( end - range . start ) . bytes ( ) ) ;
1901+ let offset = bx. const_usize ( start. bytes ( ) ) ;
1902+ let len = bx. const_usize ( ( end - start) . bytes ( ) ) ;
17641903 let ptr = bx. inbounds_ptradd ( ptr, offset) ;
17651904 bx. memset ( ptr, zero, len, Align :: ONE , MemFlags :: empty ( ) ) ;
17661905 }
@@ -1902,18 +2041,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
19022041 ) ;
19032042
19042043 // The arguments of an `extern "cmse-nonsecure-call"` function cross the secure
1905- // boundary. Zero padding bytes so information does not leak.
1906- //
1907- // This only zeroes "guaranteed" padding. There may be more bytes that are
1908- // padding for some but not all variants of this type; those are not zeroed.
1909- //
1910- // Passing an argument with value-dependent padding will instead trigger a lint.
2044+ // boundary. Clear any padding bytes so information does not leak.
19112045 if conv == CanonAbi :: Arm ( ArmCall :: CCmseNonSecureCall ) {
1912- self . zero_byte_ranges (
2046+ self . clear_padding_cmse (
19132047 bx,
19142048 llscratch,
19152049 Size :: from_bytes ( copy_bytes) ,
1916- & arg. layout . padding_ranges ( bx . cx ( ) ) ,
2050+ arg. layout ,
19172051 ) ;
19182052 }
19192053
0 commit comments