Skip to content

Commit 27ef8fe

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 e643f59 + e702f96 commit 27ef8fe

File tree

8 files changed

+68
-12
lines changed

8 files changed

+68
-12
lines changed

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);

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();

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

compiler/rustc_target/src/spec/mod.rs

+22-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};
@@ -3598,6 +3600,25 @@ impl Target {
35983600
_ => return None,
35993601
})
36003602
}
3603+
3604+
/// Returns whether this target is known to have unreliable alignment:
3605+
/// native C code for the target fails to align some data to the degree
3606+
/// required by the C standard. We can't *really* do anything about that
3607+
/// since unsafe Rust code may assume alignment any time, but we can at least
3608+
/// inhibit some optimizations, and we suppress the alignment checks that
3609+
/// would detect this unsoundness.
3610+
///
3611+
/// Every target that returns less than `Align::MAX` here is still has a soundness bug.
3612+
pub fn max_reliable_alignment(&self) -> Align {
3613+
// FIXME(#112480) MSVC on x86-32 is unsound and fails to properly align many types with
3614+
// more-than-4-byte-alignment on the stack. This makes alignments larger than 4 generally
3615+
// unreliable on 32bit Windows.
3616+
if self.is_like_windows && self.arch == "x86" {
3617+
Align::from_bytes(4).unwrap()
3618+
} else {
3619+
Align::MAX
3620+
}
3621+
}
36013622
}
36023623

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

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

src/doc/rustc/src/platform-support.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ target | notes
3434
-------|-------
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+)
37-
`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI]
37+
`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
3838
`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI]
3939
[`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+)
4040
[`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+)
@@ -43,6 +43,8 @@ target | notes
4343

4444
[^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].
4545

46+
[^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).
47+
4648
[77071]: https://github.com/rust-lang/rust/issues/77071
4749
[x86-32-float-return-issue]: https://github.com/rust-lang/rust/issues/115567
4850

@@ -95,7 +97,7 @@ target | notes
9597
[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony
9698
[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36)
9799
[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5)
98-
[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI]
100+
[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
99101
`powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17)
100102
`powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17)
101103
[`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10, glibc 2.17)
@@ -169,7 +171,7 @@ target | std | notes
169171
[`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI]
170172
[`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI]
171173
`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI]
172-
[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat)
174+
[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment]
173175
[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
174176
[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI)
175177
[`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs]
@@ -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

tests/codegen/align-struct.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
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-i686-pc-windows-msvc
4+
//@ ignore-i686-pc-windows-gnu
35

46
#![crate_type = "lib"]
57

tests/codegen/issues/issue-56927.rs

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

36
#![crate_type = "rlib"]
47

0 commit comments

Comments
 (0)