Skip to content

Commit ae95269

Browse files
committed
Auto merge of rust-lang#139261 - RalfJung:msvc-align-mitigation, r=<try>
mitigate MSVC alignment issue on x86-32 This implements mitigation for rust-lang#112480 by stopping to emit `align` attributes on loads and function arguments when building for a win32 MSVC target. MSVC is known to not properly align `u64` and similar types, and claiming to LLVM that everything is properly aligned increases the chance that this will cause problems. Of course, the misalignment is still a bug, but we can't fix that bug, only MSVC can. Also add an errata note to the platform support page warning users about this known problem. try-job: `i686-msvc*`
2 parents b8ff7b6 + df69e5d commit ae95269

File tree

8 files changed

+63
-10
lines changed

8 files changed

+63
-10
lines changed

Diff for: compiler/rustc_codegen_llvm/src/builder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
594594
fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value {
595595
unsafe {
596596
let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED);
597+
let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment());
597598
llvm::LLVMSetAlignment(load, align.bytes() as c_uint);
598599
load
599600
}
@@ -807,6 +808,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
807808
assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer);
808809
unsafe {
809810
let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr);
811+
let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment());
810812
let align =
811813
if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() as c_uint };
812814
llvm::LLVMSetAlignment(store, align);

Diff for: compiler/rustc_mir_transform/src/check_alignment.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_abi::Align;
12
use rustc_index::IndexVec;
23
use rustc_middle::mir::interpret::Scalar;
34
use rustc_middle::mir::visit::PlaceContext;
@@ -11,10 +12,6 @@ pub(super) struct CheckAlignment;
1112

1213
impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
1314
fn is_enabled(&self, sess: &Session) -> bool {
14-
// FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows
15-
if sess.target.llvm_target == "i686-pc-windows-msvc" {
16-
return false;
17-
}
1815
sess.ub_checks()
1916
}
2017

@@ -87,6 +84,33 @@ fn insert_alignment_check<'tcx>(
8784
))),
8885
});
8986

87+
// If this target does not have reliable alignment, further limit the mask by anding it with
88+
// the mask for the highest reliable alignment.
89+
#[allow(irrefutable_let_patterns)]
90+
if let max_align = tcx.sess.target.max_reliable_alignment()
91+
&& max_align < Align::MAX
92+
{
93+
let max_mask = max_align.bytes() - 1;
94+
let max_mask = Operand::Constant(Box::new(ConstOperand {
95+
span: source_info.span,
96+
user_ty: None,
97+
const_: Const::Val(
98+
ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)),
99+
tcx.types.usize,
100+
),
101+
}));
102+
stmts.push(Statement {
103+
source_info,
104+
kind: StatementKind::Assign(Box::new((
105+
alignment_mask,
106+
Rvalue::BinaryOp(
107+
BinOp::BitAnd,
108+
Box::new((Operand::Copy(alignment_mask), max_mask)),
109+
),
110+
))),
111+
});
112+
}
113+
90114
// BitAnd the alignment mask with the pointer
91115
let alignment_bits =
92116
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();

Diff for: compiler/rustc_target/src/callconv/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ pub struct ArgAttributes {
144144
/// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be
145145
/// set on a null pointer, but all non-null pointers must be dereferenceable).
146146
pub pointee_size: Size,
147+
/// The minimum alignment of the pointee, if any.
147148
pub pointee_align: Option<Align>,
148149
}
149150

Diff for: compiler/rustc_target/src/spec/mod.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ use std::path::{Path, PathBuf};
4242
use std::str::FromStr;
4343
use std::{fmt, io};
4444

45-
use rustc_abi::{Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors};
45+
use rustc_abi::{
46+
Align, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors,
47+
};
4648
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
4749
use rustc_fs_util::try_canonicalize;
4850
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
@@ -3602,6 +3604,24 @@ impl Target {
36023604
_ => return None,
36033605
})
36043606
}
3607+
3608+
/// Returns whether this target is known to have unreliable alignment:
3609+
/// native C code for the target fails to align some data to the degree
3610+
/// required by the C standard. We can't *really* do anything about that
3611+
/// since unsafe Rust code may assume alignment any time, but we can at least
3612+
/// inhibit some optimizations, and we suppress the alignment checks that
3613+
/// would detect this unsoundness.
3614+
///
3615+
/// Every target that returns less than `Align::MAX` here is still has a soundness bug.
3616+
pub fn max_reliable_alignment(&self) -> Align {
3617+
// FIXME(#112480) MSVC on x86-32 is unsound and fails to properly align many types with
3618+
// more-than-4-byte-alignment on the stack.
3619+
if self.is_like_msvc && self.arch == "x86" {
3620+
Align::from_bytes(4).unwrap()
3621+
} else {
3622+
Align::MAX
3623+
}
3624+
}
36053625
}
36063626

36073627
/// Either a target tuple string or a path to a JSON file.

Diff for: compiler/rustc_ty_utils/src/abi.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ fn adjust_for_rust_scalar<'tcx>(
347347
None
348348
};
349349
if let Some(kind) = kind {
350-
attrs.pointee_align = Some(pointee.align);
350+
attrs.pointee_align =
351+
Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment()));
351352

352353
// `Box` are not necessarily dereferenceable for the entire duration of the function as
353354
// they can be deallocated at any time. Same for non-frozen shared references (see

Diff for: src/doc/rustc/src/platform-support.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ target | notes
3535
[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
3636
`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+)
3737
`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI]
38-
`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI]
38+
`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
3939
`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI]
4040
[`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+)
4141
`x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+, Windows Server 2016+)
@@ -44,6 +44,8 @@ target | notes
4444

4545
[^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue].
4646

47+
[^win32-msvc-alignment]: Due to non-standard behavior of MSVC, native C code on this target can cause types with an alignment of more than 4 bytes to be incorrectly aligned to only 4 bytes (this affects, e.g., `u64` and `i64`). Rust applies some mitigations to reduce the impact of this issue, but this can still cause unsoundness due to unsafe code that (correctly) assumes that references are always properly aligned. See [issue #112480](https://github.com/rust-lang/rust/issues/112480).
48+
4749
[77071]: https://github.com/rust-lang/rust/issues/77071
4850
[x86-32-float-return-issue]: https://github.com/rust-lang/rust/issues/115567
4951

@@ -317,9 +319,9 @@ target | std | host | notes
317319
[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI]
318320
[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI]
319321
`i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI]
320-
[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI]
322+
[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
321323
[`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI]
322-
[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI]
324+
[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
323325
[`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI]
324326
[`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony
325327
[`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux

Diff for: tests/codegen/align-struct.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0
2-
//
2+
// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480)
3+
//@ ignore-msvc
34

45
#![crate_type = "lib"]
56

Diff for: tests/codegen/issues/issue-56927.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
//@ compile-flags: -C no-prepopulate-passes
2+
// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480)
3+
//@ ignore-msvc
24

35
#![crate_type = "rlib"]
46

0 commit comments

Comments
 (0)