Repository navigation
Description
This is a regression from #98112. I suppose it's not possible to disable this specific check only while preserving debug assertions...
The core problem is that the x86 ABI on Windows doesn't guarantee the stack alignment above 4. See for example https://developercommunity.visualstudio.com/t/vs2017-64-bit-int-alignment-problem/294259
And while some types have an alignment reported of 8 (e.g. UINT64), in practice, the C compiler will happily not align them on the stack.
So for example, this C code, compiled by MSVC for 32-bits:
#include <cinttypes>
extern void hoge(uint64_t*);
void foo() {
uint64_t a;
hoge(&a);
}
will produce this assembly:
_a$ = -8 ; size = 8
void foo(void) PROC ; foo
push ebp
mov ebp, esp
sub esp, 8
lea eax, DWORD PTR _a$[ebp]
push eax
call void hoge(unsigned __int64 *) ; hoge
add esp, 4
mov esp, ebp
pop ebp
ret 0
void foo(void) ENDP
If the stack pointer is not 8-bytes aligned when entering the function, the pointer passed to hoge is not going to be 8-bytes aligned.
As mentioned in the linked community post above, adding alignas(8) to the type definition makes the compiler align the stack:
#include <cinttypes>
extern void hoge(uint64_t*);
void foo() {
alignas(8) uint64_t a;
hoge(&a);
}
becomes
_a$ = -8 ; size = 8
void foo(void) PROC ; foo
push ebx
mov ebx, esp
sub esp, 8
and esp, -8 ; fffffff8H
add esp, 4
push ebp
mov ebp, DWORD PTR [ebx+4]
mov DWORD PTR [esp+4], ebp
mov ebp, esp
sub esp, 8
lea eax, DWORD PTR _a$[ebp]
push eax
call void hoge(unsigned __int64 *) ; hoge
add esp, 4
mov esp, ebp
pop ebp
mov esp, ebx
pop ebx
ret 0
void foo(void) ENDP
Now, what this means is that if that hoge function is a rust FFI function, and it uses that pointer, the "misaligned pointer dereference" check is hit and panic ensues.
Real life case, for the curious:
https://github.com/servo/dwrote-rs/blob/master/src/font_file_loader_impl.rs#L116-L123
That function is called from dwrite.dll (which comes with Windows).
t-opsem FCP comment
Summary of the MSVC alignment rules
Activity
glandium commentedon Jun 10, 2023
Let me substantiate this:
compiles to:
(on godbolt)
Actually, if the stack pointer is 8-bytes aligned when entering the function, the pointer passed to hoge is going to be unaligned (because there's a push first).
saethlin commentedon Jun 10, 2023
If you can pass unstable flags, currently, you can say
-Zmir-enable-passes=-CheckAlignment.saethlin commentedon Jun 10, 2023
I think your report here is a duplicate of https://developercommunity.visualstudio.com/t/visual-c-alignof-is-not-conformant-on-x86/1258506
That is, this appears to be a known MSVC bug.
glandium commentedon Jun 10, 2023
The MSVC bug would be the value returned by alignof. That doesn't change anything for the alignment check in rust.
saethlin commentedon Jun 10, 2023
std::mem::align_of::<u64>()returns8on i686-pc-windows-msvc. Are you saying that is a bug?ChrisDenton commentedon Jun 10, 2023
It's complicated. See https://learn.microsoft.com/en-us/cpp/c-language/alignment-c?view=msvc-170
So a type with a preferred alignment of 8 bytes can be 4-byte aligned on 32-bit targets.
saethlin commentedon Jun 10, 2023
Ah, but
std::mem::align_ofdoes not return the preferred alignment.ChrisDenton commentedon Jun 10, 2023
But see also https://learn.microsoft.com/en-us/cpp/build/reference/zp-struct-member-alignment?view=msvc-170
Where
Nis 8 for x86.glandium commentedon Jun 10, 2023
It's true that from the perspective that in
struct { int a; uint64_t b; }, b is 8-bytes aligned, alignof returning 8 is actually correct. But it's more indicative of "you'll be safe if you align to 8 bytes" than "you must align to 8 bytes" or "it will always be aligned to 8 bytes". Because the struct itself, on the stack, will still be 4-bytes aligned. The x86 windows ABI is weird that way... but that essentially breaks the assumption from the alignment check.ChrisDenton commentedon Jun 10, 2023
Right. But it matters in terms of how this is addressed. It would be wrong to change
align_ofas that's documented to be for struct fields.For the short term, skipping this check on i686-windows might be the only decent options (which is unfortunate). But longer term it would be better to have a proper fix. I'm not really what that would look like tbh. As you say, it's breaking a core assumption.
saethlin commentedon Jun 10, 2023
There is no assumption made by the alignment check. We unambiguously tell LLVM that a dereference of a
*const u64oni686-pc-windows-msvcis 8-byte-aligned: https://godbolt.org/z/MrdTPqWbYIf you are saying that this alignment assumption is wrong, then we have a soundness issue. The bug is not in the alignment check, we either have invalid codegen from MSVC because it does not sufficiently align its
uint64_ts, or we have invalid codegen from rustc because it assumesu64is 8-byte-aligned on this target when it is not.85 remaining items
Rollup merge of rust-lang#139261 - RalfJung:msvc-align-mitigation, r=…
Rollup merge of rust-lang#139261 - RalfJung:msvc-align-mitigation, r=…
Rollup merge of rust-lang#139261 - RalfJung:msvc-align-mitigation, r=…
Unrolled build for rust-lang#139261
Rollup merge of #139261 - RalfJung:msvc-align-mitigation, r=oli-obk
Rollup merge of #139261 - RalfJung:msvc-align-mitigation, r=oli-obk
Jules-Bertholet commentedon Jun 11, 2025
https://learn.microsoft.com/en-us/cpp/c-language/alignment-c says (emphasis mine):
Let’s compare to the C standard (emphasis again mine):
I think the correct interpretation here is that x86-32 MSVC’s
alignofis not standard-compliant. MSVC only requiresuint64_tto be aligned to 4 bytes (or maybe even less), but prefers it to be aligned to 8.alignof/alignason that compiler use this preferred alignment, in contravention of the standard, but that does not reflect the minimum alignment required to avoid UB.RalfJung commentedon Jun 12, 2025
It is somewhat ironic that the same section of the docs proclaims some practices that help portability, while also describing a practice that can easily lead to portable code having UB (since portable C code could easily rely on
alignofreturning the required alignment of a type for correctness).briansmith commentedon Jun 20, 2025
Note that this is the same for GCC until GCC 8: https://gcc.gnu.org/gcc-8/porting_to.html#alignof
mirh commentedon Jul 5, 2025
FWIW gcc has still its own bugs
Jules-Bertholet commentedon May 5, 2026
A "fun" interaction between this issue and other MSVC weirdness: #81996 (comment)
Jules-Bertholet commentedon May 5, 2026
In fact, thanks to this weirdness, we can more or less polyfill a standard-compliant
alignofmacro in MSVC: https://godbolt.org/z/4M81KGhdMglandium commentedon May 7, 2026
Does clang-cl output the same thing?
Jules-Bertholet commentedon May 8, 2026
Yes.