Skip to content

Commit 6f62799

Browse files
committed
Auto merge of #159466 - folkertdev:clear-variant-dependent-padding, r=<try>
cmse: clear variant-dependent padding in `enum`s try-job: x86_64-gnu-llvm-22-*
2 parents 390279b + 7460074 commit 6f62799

4 files changed

Lines changed: 540 additions & 67 deletions

File tree

compiler/rustc_abi/src/layout/ty.rs

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,35 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
284284
found
285285
}
286286

287+
/// Whether this type/layout has any padding that is dependent on a variant, i.e. has bytes that
288+
/// are padding for some, but not all, valid values of this type.
289+
pub fn has_variant_dependent_padding<C>(&self, cx: &C) -> bool
290+
where
291+
Ty: TyAbiInterface<'a, C> + Copy,
292+
{
293+
match self.variants {
294+
Variants::Multiple { .. } => true,
295+
Variants::Empty => false,
296+
Variants::Single { .. } => match &self.fields {
297+
FieldsShape::Primitive | FieldsShape::Union(_) => false,
298+
FieldsShape::Array { count, .. } => {
299+
*count > 0 && self.field(cx, 0).has_variant_dependent_padding(cx)
300+
}
301+
FieldsShape::Arbitrary { offsets, .. } => {
302+
(0..offsets.len()).any(|i| self.field(cx, i).has_variant_dependent_padding(cx))
303+
}
304+
},
305+
}
306+
}
307+
287308
/// The ranges of bytes that are always ignored by the representation relation of this type.
288309
///
289310
/// In other words, for any sequence of bytes, if we reset the these padding bytes to uninit,
290311
/// then these two sequences of bytes represent the same value (or they are both invalid).
291312
/// This is the "guaranteed" padding. There may be more bytes that are padding for some
292313
/// but not all variants of this type; those are not included.
293314
/// (E.g. `Option<i8>` has no guaranteed padding so the empty range set is returned, but its `None` value still has padding).
294-
pub fn padding_ranges<C>(&self, cx: &C) -> Vec<Range<Size>>
315+
pub fn variant_independent_padding_ranges<C>(&self, cx: &C) -> Vec<Range<Size>>
295316
where
296317
Ty: TyAbiInterface<'a, C> + Copy,
297318
{
@@ -316,6 +337,45 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
316337
uninit_ranges
317338
}
318339

340+
/// The ranges of bytes that are ignored by the representation relation of this variant.
341+
///
342+
/// The result does not include variant-independent padding.
343+
pub fn variant_dependent_padding_ranges<C>(
344+
&self,
345+
cx: &C,
346+
variant_index: VariantIdx,
347+
) -> Vec<Range<Size>>
348+
where
349+
Ty: TyAbiInterface<'a, C> + Copy,
350+
{
351+
let Variants::Multiple { .. } = self.variants else {
352+
return Vec::new();
353+
};
354+
355+
// Bytes that are data in some variant.
356+
let mut any = RangeSet::new();
357+
self.add_data_ranges(cx, Size::ZERO, &mut any);
358+
359+
// Bytes that are data in this variant.
360+
let mut this = RangeSet::new();
361+
362+
// The variants do not contain e.g. the discriminant or coroutine upvars.
363+
let FieldsShape::Arbitrary { offsets, in_memory_order: _ } = &self.fields else {
364+
unreachable!("a multi-variant layout should have `Arbitrary` fields")
365+
};
366+
367+
// So add them explicitly.
368+
for (field, &offset) in offsets.iter_enumerated() {
369+
let field = self.field(cx, field.as_usize());
370+
field.add_data_ranges(cx, offset, &mut this);
371+
}
372+
373+
self.for_variant(cx, variant_index).add_data_ranges(cx, Size::ZERO, &mut this);
374+
375+
// Padding specific to this variant: data in some variant, but not in this one.
376+
any.difference(&this).0.iter().map(|&(offset, size)| offset..offset + size).collect()
377+
}
378+
319379
/// Extend `out` with all ranges of bytes that *may* carry relevant data for values of this type.
320380
/// For enums and unions there are offsets that are initialized for some
321381
/// variants but not for others; those offset *will* get added to `out`.
@@ -327,39 +387,42 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
327387
return;
328388
}
329389

330-
match &self.variants {
331-
Variants::Empty => { /* done */ }
332-
Variants::Single { index: _ } => match &self.fields {
333-
FieldsShape::Primitive => {
334-
out.add_range(base_offset, self.size);
335-
}
336-
&FieldsShape::Union(field_count) => {
337-
for field in 0..field_count.get() {
338-
let field = self.field(cx, field);
339-
field.add_data_ranges(cx, base_offset, out);
340-
}
390+
// Visit the fields of this value. For enum values the fields include the discriminant.
391+
match &self.fields {
392+
FieldsShape::Primitive => {
393+
out.add_range(base_offset, self.size);
394+
}
395+
&FieldsShape::Union(field_count) => {
396+
for field in 0..field_count.get() {
397+
let field = self.field(cx, field);
398+
field.add_data_ranges(cx, base_offset, out);
341399
}
342-
&FieldsShape::Array { stride, count } => {
343-
let elem = self.field(cx, 0);
344-
345-
// For scalars we know there is no padding between the elements,
346-
// so the entire array is a single big data range.
347-
if elem.backend_repr.is_scalar() {
348-
out.add_range(base_offset, elem.size * count);
349-
} else {
350-
// FIXME: this is really inefficient for large arrays.
351-
for idx in 0..count {
352-
elem.add_data_ranges(cx, base_offset + idx * stride, out);
353-
}
400+
}
401+
&FieldsShape::Array { stride, count } => {
402+
let elem = self.field(cx, 0);
403+
404+
// For scalars we know there is no padding between the elements,
405+
// so the entire array is a single big data range.
406+
if elem.backend_repr.is_scalar() {
407+
out.add_range(base_offset, elem.size * count);
408+
} else {
409+
// FIXME: this is really inefficient for large arrays.
410+
for idx in 0..count {
411+
elem.add_data_ranges(cx, base_offset + idx * stride, out);
354412
}
355413
}
356-
FieldsShape::Arbitrary { offsets, in_memory_order: _ } => {
357-
for (field, &offset) in offsets.iter_enumerated() {
358-
let field = self.field(cx, field.as_usize());
359-
field.add_data_ranges(cx, base_offset + offset, out);
360-
}
414+
}
415+
FieldsShape::Arbitrary { offsets, in_memory_order: _ } => {
416+
for (field, &offset) in offsets.iter_enumerated() {
417+
let field = self.field(cx, field.as_usize());
418+
field.add_data_ranges(cx, base_offset + offset, out);
361419
}
362-
},
420+
}
421+
}
422+
423+
// Visit the fields of each variant.
424+
match &self.variants {
425+
Variants::Empty | Variants::Single { index: _ } => { /* done */ }
363426
Variants::Multiple { variants, .. } => {
364427
for variant in variants.indices() {
365428
let variant = self.for_variant(cx, variant);

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 155 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::cmp;
22
use std::ops::Range;
33

44
use 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
};
78
use rustc_ast as ast;
89
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
@@ -11,7 +12,7 @@ use rustc_hir::attrs::AttributeKind;
1112
use rustc_hir::lang_items::LangItem;
1213
use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER;
1314
use 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};
1516
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
1617
use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt};
1718
use 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

Comments
 (0)