This is an archive of the discontinued LLVM Phabricator instance.

[UBSan] Fix incorrect alignment reported when global new returns an offset pointer
Needs ReviewPublic

Authored by belkiss on Jan 8 2022, 7:26 AM.

Details

Summary

I found that ubsan will report an incorrect alignment for a type in case it is allocated with the global operator new (without alignment), if we have it return an offset ptr.

I wrote a small repro: https://godbolt.org/z/n8Yh8eoaE

The type is aligned on 8 bytes (verified by static_assert on its alignof), but ubsan reports: "constructor call on misaligned address 0x000002af8fd8 for type 'Param', which requires 16 byte alignment".

Now I suppose changing the ptr returned by new that way breaks the __STDCPP_DEFAULT_NEW_ALIGNMENT__, but in the specs in [basic.stc.dynamic.allocation] it says for the non-aligned, non array new:

Otherwise, the storage is aligned for any object that does not have new-extended alignment and is of the requested size

which is pretty vague.

I would either expect to get an error message to indicate that break, or nothing, because in the end the pointer returned by new is 8 bytes aligned, and matches the 8 bytes alignment requirement of the type.

I think the issue comes from this line:

https://github.com/llvm/llvm-project/blob/4f7fb13f87e10bd2cd89ccf2be70b026032237a7/clang/lib/CodeGen/CGExprCXX.cpp#L1737

Instead of the allocator alignment result.getAlignment(), it should be the type alignment allocAlign. I've tried it, and ran the tests, the error goes away and the tests pass.

Fixes https://github.com/llvm/llvm-project/issues/51035

Diff Detail

Event Timeline

belkiss requested review of this revision.Jan 8 2022, 7:26 AM
belkiss created this revision.
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptJan 8 2022, 7:26 AM
Herald added subscribers: Restricted Project, cfe-commits. · View Herald Transcript
belkiss edited the summary of this revision. (Show Details)Jan 8 2022, 7:28 AM
belkiss added a reviewer: rsmith.
belkiss edited the summary of this revision. (Show Details)
belkiss updated this revision to Diff 398346.Jan 8 2022, 10:07 AM

Disable the test in ubsan-msan and ubsan-tsan since they also override global new, causing link error.

belkiss updated this revision to Diff 398350.Jan 8 2022, 10:12 AM

Squash commits, for some reason update dropped the first commit (first time using arcanist...).

belkiss updated this revision to Diff 398351.Jan 8 2022, 10:47 AM

Clang-format

belkiss updated this revision to Diff 398369.Jan 8 2022, 1:06 PM

Use static_cast/reinterpret_cast instead of C-casts
This will also retry the CI, apparently fuzzer-finalstats.test is flaky

aganea added subscribers: hans, rnk, aganea.

+@hans @rnk Since we discussed this in one of the past Windows/COFF calls.

I'm not familiar enough with the code to review it (though it sounds right to me). Looks like it was written by Serge originally.

rnk added a subscriber: rjmccall.Jan 13 2022, 10:58 AM

I think the main thing here is that the diagnostic is incorrect: It claims that the type requires __STDC_DEFAULT_NEW_ALIGNMENT__ (16) byte alignment, but it doesn't, it only requires 8. That's confusing.

The next question is, does LLVM exploit __STDCPP_DEFAULT_NEW_ALIGNMENT__ for optimization purposes? IMO, UBSan should agree with LLVM: it is supposed to aid in the early detection of UB, rather than having to figure that out later after optimizations have run. If LLVM doesn't exploit it, I don't see a need to report an error. After some experimentation, I have convinced myself that LLVM doesn't know or exploit allocator alignment:
https://godbolt.org/z/oo6rzKn3c

Finally, we should ask ourselves, is it worth adding a special diagnostic to check that all operator new calls return aligned pointers? I think the answer is no. Users are more likely to heed sanitizer errors when they point to actual, practical issues rather than hypothetical ones about more highly aligned types allocated at other call sites.

Let me ask @rjmccall for a second opinion, since he wrote the Address alignment-tracking overhaul code.

compiler-rt/test/ubsan/TestCases/TypeCheck/global-new-alignment.cpp
34

I suggest simplifying this to CHECK-NOT: runtime error, since no errors are expected. This is mostly to improve test debuggability anyway, the test will fail if it exits non-zero.

belkiss added inline comments.Jan 13 2022, 11:06 AM
compiler-rt/test/ubsan/TestCases/TypeCheck/global-new-alignment.cpp
34

I'll remove it altogether from here, since I pass --implicit-check-not="runtime error" to the FileCheck at the top of the file

belkiss updated this revision to Diff 399764.Jan 13 2022, 1:20 PM

Simplify test, remove the inline specific CHECK-NOT since we have the implicit-check-not in FileCheck args checking that no errors have occured in the whole file

belkiss marked an inline comment as done.Jan 13 2022, 1:21 PM

And thanks @aganea, @hans & @rnk!

If we want to assume a weaker alignment, we should change the calculation of allocationAlign rather than just changing this point downstream of that computation. But replacements of the standard operator new are restricted and cannot simply choose to return less-aligned memory; at the very least, the test program is not portable.

rnk added inline comments.Feb 8 2022, 4:15 PM
clang/lib/CodeGen/CGExprCXX.cpp
1658–1659

I believe John's proposal is to remove this max here.

rjmccall added inline comments.Feb 8 2022, 6:05 PM
clang/lib/CodeGen/CGExprCXX.cpp
1658–1659

Or to at least use something more conservative, like the alignment of a pointer. Or even potentially to fix getNewAlign() so that we're only aggressive about it on targets (like Darwin) that have more explicitly authorized us to be so. But in any case, we should make the change up here instead of only in the UBSan check.