https://github.com/gbMattN updated https://github.com/llvm/llvm-project/pull/166755
>From 337b0d0d8d517a056eea440ae30cf9edab4f3a6a Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Fri, 17 Oct 2025 13:49:55 +0100 Subject: [PATCH 1/7] [UBSan] Report more detailed alignment report when overloaded global new returns incorrectly aligned memory --- clang/lib/CodeGen/CGExprCXX.cpp | 21 +++++++++- clang/lib/CodeGen/CodeGenFunction.h | 5 ++- compiler-rt/lib/ubsan/ubsan_checks.inc | 1 + compiler-rt/lib/ubsan/ubsan_handlers.cpp | 17 ++++++-- .../TestCases/TypeCheck/minimum-alignment.cpp | 39 +++++++++++++++++++ 5 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 14d8db32bafc6..37c2d47102b54 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -18,8 +18,12 @@ #include "ConstantEmitter.h" #include "TargetInfo.h" #include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/Sanitizers.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "llvm/IR/Intrinsics.h" +#include <cstdio> using namespace clang; using namespace CodeGen; @@ -1749,6 +1753,21 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { allocator->isReservedGlobalPlacementOperator()) result = Builder.CreateLaunderInvariantGroup(result); + // Check what type of constructor call the sanitizer is checking + // Different UB can occour with custom overloads of operator new + TypeCheckKind checkKind = CodeGenFunction::TCK_ConstructorCall; + const TargetInfo& TI = getContext().getTargetInfo(); + unsigned DefaultTargetAlignment = TI.getNewAlign() / TI.getCharWidth(); + SourceManager &SM = getContext().getSourceManager(); + SourceLocation Loc = E->getOperatorNew()->getLocation(); + bool IsCustomOverload = !SM.isInSystemHeader(Loc); + if ( + SanOpts.has(SanitizerKind::Alignment) && + IsCustomOverload && + (DefaultTargetAlignment > CGM.getContext().getTypeAlignInChars(allocType).getQuantity()) + ) + checkKind = CodeGenFunction::TCK_ConstructorCallOverloadedNew; + // Emit sanitizer checks for pointer value now, so that in the case of an // array it was checked only once and not at each constructor call. We may // have already checked that the pointer is non-null. @@ -1756,7 +1775,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // we'll null check the wrong pointer here. SanitizerSet SkippedChecks; SkippedChecks.set(SanitizerKind::Null, nullCheck); - EmitTypeCheck(CodeGenFunction::TCK_ConstructorCall, + EmitTypeCheck(checkKind, E->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), result, allocType, result.getAlignment(), SkippedChecks, numElements); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 8c4c1c8c2dc95..2200715a79ea5 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3296,7 +3296,10 @@ class CodeGenFunction : public CodeGenTypeCache { TCK_NonnullAssign, /// Checking the operand of a dynamic_cast or a typeid expression. Must be /// null or an object within its lifetime. - TCK_DynamicOperation + TCK_DynamicOperation, + /// Checking the 'this' poiner for a constructor call, including that the + /// alignment is greater or equal to the targets minimum alignment + TCK_ConstructorCallOverloadedNew }; /// Determine whether the pointer type check \p TCK permits null pointers. diff --git a/compiler-rt/lib/ubsan/ubsan_checks.inc b/compiler-rt/lib/ubsan/ubsan_checks.inc index b1d09a9024e7e..df3ef0f595659 100644 --- a/compiler-rt/lib/ubsan/ubsan_checks.inc +++ b/compiler-rt/lib/ubsan/ubsan_checks.inc @@ -28,6 +28,7 @@ UBSAN_CHECK(NullptrAfterNonZeroOffset, "nullptr-after-nonzero-offset", UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow") UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment") UBSAN_CHECK(AlignmentAssumption, "alignment-assumption", "alignment") +UBSAN_CHECK(AlignmentOnOverloadedNew, "alignment-new", "alignment") UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size") UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow", "signed-integer-overflow") diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp index 63319f46734a4..a16d2c6879907 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp @@ -73,14 +73,17 @@ enum TypeCheckKind { TCK_NonnullAssign, /// Checking the operand of a dynamic_cast or a typeid expression. Must be /// null or an object within its lifetime. - TCK_DynamicOperation + TCK_DynamicOperation, + /// Checking the 'this' poiner for a constructor call, including that the + /// alignment is greater or equal to the targets minimum alignment + TCK_ConstructorCallOverloadedNew }; extern const char *const TypeCheckKinds[] = { "load of", "store to", "reference binding to", "member access within", "member call on", "constructor call on", "downcast of", "downcast of", "upcast of", "cast to virtual base of", "_Nonnull binding to", - "dynamic operation on"}; + "dynamic operation on", "constructor call with pointer from overloaded operator new on"}; } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, @@ -94,7 +97,9 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, ? ErrorType::NullPointerUseWithNullability : ErrorType::NullPointerUse; else if (Pointer & (Alignment - 1)) - ET = ErrorType::MisalignedPointerUse; + ET = (Data->TypeCheckKind == TCK_ConstructorCallOverloadedNew) + ? ErrorType::AlignmentOnOverloadedNew + : ErrorType::MisalignedPointerUse; else ET = ErrorType::InsufficientObjectSize; @@ -117,6 +122,12 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, Diag(Loc, DL_Error, ET, "%0 null pointer of type %1") << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; break; + case ErrorType::AlignmentOnOverloadedNew: + Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %2, " + "which requires target minimum assumed alignment of %3") + << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer + << Data->Type << Alignment; + break; case ErrorType::MisalignedPointerUse: Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %3, " "which requires %2 byte alignment") diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp new file mode 100644 index 0000000000000..8f9e7f652bafe --- /dev/null +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp @@ -0,0 +1,39 @@ +// RUN: %clangxx %gmlt -fsanitize=alignment %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// UNSUPPORTED: i386 +// UNSUPPORTED: armv7l + +// These sanitizers already overload the new operator so won't compile this test +// UNSUPPORTED: ubsan-msan +// UNSUPPORTED: ubsan-tsan + +#include <cassert> +#include <cstdlib> + +void* operator new(std::size_t count) +{ + constexpr const size_t offset = 8; + + // allocate a bit more so we can safely offset it + void* ptr = std::malloc(count + offset); + + // verify malloc returned 16 bytes aligned mem + static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16); + assert(((std::ptrdiff_t)ptr & (__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1)) == 0); + + return (char*)ptr + offset; +} + +struct Foo +{ + void* _cookie1, *_cookie2; +}; + +static_assert(alignof(Foo) == 8); +int main() +{ + // CHECK: runtime error: constructor call with pointer from overloaded operator new on misaligned address 0x{{.*}} for type 'Foo', which requires target minimum assumed alignment of 16 + Foo* f = new Foo; + return 0; +} \ No newline at end of file >From 043ea10885c5743777c6b7f191d6ef74a952743b Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Fri, 17 Oct 2025 14:02:39 +0100 Subject: [PATCH 2/7] Tidy --- clang/lib/CodeGen/CGExprCXX.cpp | 20 +++++------ clang/lib/CodeGen/CodeGenFunction.h | 2 +- compiler-rt/lib/ubsan/ubsan_handlers.cpp | 32 +++++++++++------ .../TestCases/TypeCheck/minimum-alignment.cpp | 35 +++++++++---------- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 37c2d47102b54..52d67be7f634a 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -23,7 +23,6 @@ #include "clang/Basic/SourceManager.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "llvm/IR/Intrinsics.h" -#include <cstdio> using namespace clang; using namespace CodeGen; @@ -1756,18 +1755,16 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // Check what type of constructor call the sanitizer is checking // Different UB can occour with custom overloads of operator new TypeCheckKind checkKind = CodeGenFunction::TCK_ConstructorCall; - const TargetInfo& TI = getContext().getTargetInfo(); + const TargetInfo &TI = getContext().getTargetInfo(); unsigned DefaultTargetAlignment = TI.getNewAlign() / TI.getCharWidth(); SourceManager &SM = getContext().getSourceManager(); SourceLocation Loc = E->getOperatorNew()->getLocation(); bool IsCustomOverload = !SM.isInSystemHeader(Loc); - if ( - SanOpts.has(SanitizerKind::Alignment) && - IsCustomOverload && - (DefaultTargetAlignment > CGM.getContext().getTypeAlignInChars(allocType).getQuantity()) - ) + if (SanOpts.has(SanitizerKind::Alignment) && IsCustomOverload && + (DefaultTargetAlignment > + CGM.getContext().getTypeAlignInChars(allocType).getQuantity())) checkKind = CodeGenFunction::TCK_ConstructorCallOverloadedNew; - + // Emit sanitizer checks for pointer value now, so that in the case of an // array it was checked only once and not at each constructor call. We may // have already checked that the pointer is non-null. @@ -1775,10 +1772,9 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // we'll null check the wrong pointer here. SanitizerSet SkippedChecks; SkippedChecks.set(SanitizerKind::Null, nullCheck); - EmitTypeCheck(checkKind, - E->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), - result, allocType, result.getAlignment(), SkippedChecks, - numElements); + EmitTypeCheck( + checkKind, E->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), + result, allocType, result.getAlignment(), SkippedChecks, numElements); EmitNewInitializer(*this, E, allocType, elementTy, result, numElements, allocSizeWithoutCookie); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 2200715a79ea5..06e0734e2f2ea 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3297,7 +3297,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// Checking the operand of a dynamic_cast or a typeid expression. Must be /// null or an object within its lifetime. TCK_DynamicOperation, - /// Checking the 'this' poiner for a constructor call, including that the + /// Checking the 'this' poiner for a constructor call, including that the /// alignment is greater or equal to the targets minimum alignment TCK_ConstructorCallOverloadedNew }; diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp index a16d2c6879907..5896b93700e6b 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp @@ -74,16 +74,25 @@ enum TypeCheckKind { /// Checking the operand of a dynamic_cast or a typeid expression. Must be /// null or an object within its lifetime. TCK_DynamicOperation, - /// Checking the 'this' poiner for a constructor call, including that the + /// Checking the 'this' poiner for a constructor call, including that the /// alignment is greater or equal to the targets minimum alignment TCK_ConstructorCallOverloadedNew }; extern const char *const TypeCheckKinds[] = { - "load of", "store to", "reference binding to", "member access within", - "member call on", "constructor call on", "downcast of", "downcast of", - "upcast of", "cast to virtual base of", "_Nonnull binding to", - "dynamic operation on", "constructor call with pointer from overloaded operator new on"}; + "load of", + "store to", + "reference binding to", + "member access within", + "member call on", + "constructor call on", + "downcast of", + "downcast of", + "upcast of", + "cast to virtual base of", + "_Nonnull binding to", + "dynamic operation on", + "constructor call with pointer from overloaded operator new on"}; } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, @@ -98,8 +107,8 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, : ErrorType::NullPointerUse; else if (Pointer & (Alignment - 1)) ET = (Data->TypeCheckKind == TCK_ConstructorCallOverloadedNew) - ? ErrorType::AlignmentOnOverloadedNew - : ErrorType::MisalignedPointerUse; + ? ErrorType::AlignmentOnOverloadedNew + : ErrorType::MisalignedPointerUse; else ET = ErrorType::InsufficientObjectSize; @@ -123,10 +132,11 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; break; case ErrorType::AlignmentOnOverloadedNew: - Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %2, " - "which requires target minimum assumed alignment of %3") - << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer - << Data->Type << Alignment; + Diag(Loc, DL_Error, ET, + "%0 misaligned address %1 for type %2, " + "which requires target minimum assumed alignment of %3") + << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type + << Alignment; break; case ErrorType::MisalignedPointerUse: Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %3, " diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp index 8f9e7f652bafe..2204ff361672d 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp @@ -1,7 +1,7 @@ // RUN: %clangxx %gmlt -fsanitize=alignment %s -o %t // RUN: %run %t 2>&1 | FileCheck %s -// UNSUPPORTED: i386 +// UNSUPPORTED: i386 // UNSUPPORTED: armv7l // These sanitizers already overload the new operator so won't compile this test @@ -11,29 +11,26 @@ #include <cassert> #include <cstdlib> -void* operator new(std::size_t count) -{ - constexpr const size_t offset = 8; +void *operator new(std::size_t count) { + constexpr const size_t offset = 8; - // allocate a bit more so we can safely offset it - void* ptr = std::malloc(count + offset); + // allocate a bit more so we can safely offset it + void *ptr = std::malloc(count + offset); - // verify malloc returned 16 bytes aligned mem - static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16); - assert(((std::ptrdiff_t)ptr & (__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1)) == 0); + // verify malloc returned 16 bytes aligned mem + static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16); + assert(((std::ptrdiff_t)ptr & (__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1)) == 0); - return (char*)ptr + offset; + return (char *)ptr + offset; } -struct Foo -{ - void* _cookie1, *_cookie2; +struct Foo { + void *_cookie1, *_cookie2; }; static_assert(alignof(Foo) == 8); -int main() -{ - // CHECK: runtime error: constructor call with pointer from overloaded operator new on misaligned address 0x{{.*}} for type 'Foo', which requires target minimum assumed alignment of 16 - Foo* f = new Foo; - return 0; -} \ No newline at end of file +int main() { + // CHECK: runtime error: constructor call with pointer from overloaded operator new on misaligned address 0x{{.*}} for type 'Foo', which requires target minimum assumed alignment of 16 + Foo *f = new Foo; + return 0; +} >From d01970b433e62052e10a8361d4f5fc35ac7aba55 Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Thu, 30 Oct 2025 23:13:18 +0000 Subject: [PATCH 3/7] Remove strict check and generalise error message --- clang/lib/CodeGen/CGExprCXX.cpp | 2 +- compiler-rt/lib/ubsan/ubsan_handlers.cpp | 4 ++-- .../test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp | 2 +- compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 52d67be7f634a..c4f6afa320fe3 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1760,7 +1760,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { SourceManager &SM = getContext().getSourceManager(); SourceLocation Loc = E->getOperatorNew()->getLocation(); bool IsCustomOverload = !SM.isInSystemHeader(Loc); - if (SanOpts.has(SanitizerKind::Alignment) && IsCustomOverload && + if (SanOpts.has(SanitizerKind::Alignment) && (DefaultTargetAlignment > CGM.getContext().getTypeAlignInChars(allocType).getQuantity())) checkKind = CodeGenFunction::TCK_ConstructorCallOverloadedNew; diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp index 5896b93700e6b..2aaa899afa943 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp @@ -92,7 +92,7 @@ extern const char *const TypeCheckKinds[] = { "cast to virtual base of", "_Nonnull binding to", "dynamic operation on", - "constructor call with pointer from overloaded operator new on"}; + "constructor call with pointer from operator new on"}; } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, @@ -134,7 +134,7 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, case ErrorType::AlignmentOnOverloadedNew: Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %2, " - "which requires target minimum assumed alignment of %3") + "which requires target minimum assumed %3 byte alignment") << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type << Alignment; break; diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp index 2204ff361672d..4642126ab74c4 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp @@ -30,7 +30,7 @@ struct Foo { static_assert(alignof(Foo) == 8); int main() { - // CHECK: runtime error: constructor call with pointer from overloaded operator new on misaligned address 0x{{.*}} for type 'Foo', which requires target minimum assumed alignment of 16 + // CHECK: runtime error: constructor call with pointer from operator new on misaligned address 0x{{.*}} for type 'Foo', which requires target minimum assumed 16 byte alignment Foo *f = new Foo; return 0; } diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp index e39a0ab4e6589..4b0b2b5923c6f 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp @@ -101,7 +101,7 @@ int main(int, char **argv) { return s->f() && 0; case 'n': - // CHECK-NEW: misaligned.cpp:[[@LINE+4]]{{(:21)?}}: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW: misaligned.cpp:[[@LINE+4]]{{(:21)?}}: runtime error: constructor call with pointer from operator new on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires target minimum assumed 4 byte alignment // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-NEW-NEXT: {{^ \^}} >From de077a2956c5c6997f29e33788f9799788da0cf8 Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Fri, 31 Oct 2025 16:34:29 +0000 Subject: [PATCH 4/7] Finished code removal --- clang/lib/CodeGen/CGExprCXX.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index c4f6afa320fe3..ba51f64c00f14 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1757,9 +1757,6 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { TypeCheckKind checkKind = CodeGenFunction::TCK_ConstructorCall; const TargetInfo &TI = getContext().getTargetInfo(); unsigned DefaultTargetAlignment = TI.getNewAlign() / TI.getCharWidth(); - SourceManager &SM = getContext().getSourceManager(); - SourceLocation Loc = E->getOperatorNew()->getLocation(); - bool IsCustomOverload = !SM.isInSystemHeader(Loc); if (SanOpts.has(SanitizerKind::Alignment) && (DefaultTargetAlignment > CGM.getContext().getTypeAlignInChars(allocType).getQuantity())) >From e9d3243c074d290ea9dd0797b3f89471d2787d1d Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Mon, 3 Nov 2025 14:09:26 +0000 Subject: [PATCH 5/7] Rename error type and change comment to reflect changes in checks --- clang/lib/CodeGen/CGExprCXX.cpp | 7 ++++--- clang/lib/CodeGen/CodeGenFunction.h | 2 +- compiler-rt/lib/ubsan/ubsan_checks.inc | 2 +- compiler-rt/lib/ubsan/ubsan_handlers.cpp | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index ba51f64c00f14..f2dd22e9bed3b 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1752,15 +1752,16 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { allocator->isReservedGlobalPlacementOperator()) result = Builder.CreateLaunderInvariantGroup(result); - // Check what type of constructor call the sanitizer is checking - // Different UB can occour with custom overloads of operator new + // Check the default alignment of the type and why. Users may incorrectly + // return misaligned memory from a replaced operator new without knowing + // about default alignment. TypeCheckKind checkKind = CodeGenFunction::TCK_ConstructorCall; const TargetInfo &TI = getContext().getTargetInfo(); unsigned DefaultTargetAlignment = TI.getNewAlign() / TI.getCharWidth(); if (SanOpts.has(SanitizerKind::Alignment) && (DefaultTargetAlignment > CGM.getContext().getTypeAlignInChars(allocType).getQuantity())) - checkKind = CodeGenFunction::TCK_ConstructorCallOverloadedNew; + checkKind = CodeGenFunction::TCK_ConstructorCallMinimumAlign; // Emit sanitizer checks for pointer value now, so that in the case of an // array it was checked only once and not at each constructor call. We may diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 06e0734e2f2ea..047ca844c79de 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3299,7 +3299,7 @@ class CodeGenFunction : public CodeGenTypeCache { TCK_DynamicOperation, /// Checking the 'this' poiner for a constructor call, including that the /// alignment is greater or equal to the targets minimum alignment - TCK_ConstructorCallOverloadedNew + TCK_ConstructorCallMinimumAlign }; /// Determine whether the pointer type check \p TCK permits null pointers. diff --git a/compiler-rt/lib/ubsan/ubsan_checks.inc b/compiler-rt/lib/ubsan/ubsan_checks.inc index df3ef0f595659..f8757d781afb8 100644 --- a/compiler-rt/lib/ubsan/ubsan_checks.inc +++ b/compiler-rt/lib/ubsan/ubsan_checks.inc @@ -28,7 +28,7 @@ UBSAN_CHECK(NullptrAfterNonZeroOffset, "nullptr-after-nonzero-offset", UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow") UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment") UBSAN_CHECK(AlignmentAssumption, "alignment-assumption", "alignment") -UBSAN_CHECK(AlignmentOnOverloadedNew, "alignment-new", "alignment") +UBSAN_CHECK(MinumumAssumedAlignment, "minimum-assumed-alignment", "alignment") UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size") UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow", "signed-integer-overflow") diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp index 2aaa899afa943..fc6063af4562b 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp @@ -76,7 +76,7 @@ enum TypeCheckKind { TCK_DynamicOperation, /// Checking the 'this' poiner for a constructor call, including that the /// alignment is greater or equal to the targets minimum alignment - TCK_ConstructorCallOverloadedNew + TCK_ConstructorCallMinimumAlign }; extern const char *const TypeCheckKinds[] = { @@ -106,8 +106,8 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, ? ErrorType::NullPointerUseWithNullability : ErrorType::NullPointerUse; else if (Pointer & (Alignment - 1)) - ET = (Data->TypeCheckKind == TCK_ConstructorCallOverloadedNew) - ? ErrorType::AlignmentOnOverloadedNew + ET = (Data->TypeCheckKind == TCK_ConstructorCallMinimumAlign) + ? ErrorType::MinumumAssumedAlignment : ErrorType::MisalignedPointerUse; else ET = ErrorType::InsufficientObjectSize; @@ -131,7 +131,7 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, Diag(Loc, DL_Error, ET, "%0 null pointer of type %1") << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; break; - case ErrorType::AlignmentOnOverloadedNew: + case ErrorType::MinumumAssumedAlignment: Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %2, " "which requires target minimum assumed %3 byte alignment") >From 4e32b7b7059aeffd2724e0954c045cfaa3cf26c7 Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Tue, 4 Nov 2025 16:16:51 +0000 Subject: [PATCH 6/7] Ensure we pass the right alignment to the error, and fixing failing tests --- clang/lib/CodeGen/CGExprCXX.cpp | 8 ++++++-- clang/test/CodeGenCXX/ubsan-new-checks.cpp | 4 ++-- .../test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp | 5 +++-- compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index f2dd22e9bed3b..9acf3a1817c82 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -17,6 +17,7 @@ #include "CodeGenFunction.h" #include "ConstantEmitter.h" #include "TargetInfo.h" +#include "clang/AST/CharUnits.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" @@ -1756,12 +1757,15 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // return misaligned memory from a replaced operator new without knowing // about default alignment. TypeCheckKind checkKind = CodeGenFunction::TCK_ConstructorCall; + CharUnits checkAlignment = result.getAlignment(); const TargetInfo &TI = getContext().getTargetInfo(); unsigned DefaultTargetAlignment = TI.getNewAlign() / TI.getCharWidth(); if (SanOpts.has(SanitizerKind::Alignment) && (DefaultTargetAlignment > - CGM.getContext().getTypeAlignInChars(allocType).getQuantity())) + CGM.getContext().getTypeAlignInChars(allocType).getQuantity())){ checkKind = CodeGenFunction::TCK_ConstructorCallMinimumAlign; + checkAlignment = CharUnits::fromQuantity(DefaultTargetAlignment); + } // Emit sanitizer checks for pointer value now, so that in the case of an // array it was checked only once and not at each constructor call. We may @@ -1772,7 +1776,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { SkippedChecks.set(SanitizerKind::Null, nullCheck); EmitTypeCheck( checkKind, E->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), - result, allocType, result.getAlignment(), SkippedChecks, numElements); + result, allocType, checkAlignment, SkippedChecks, numElements); EmitNewInitializer(*this, E, allocType, elementTy, result, numElements, allocSizeWithoutCookie); diff --git a/clang/test/CodeGenCXX/ubsan-new-checks.cpp b/clang/test/CodeGenCXX/ubsan-new-checks.cpp index 60edd323648ab..c8a20ecfed3b4 100644 --- a/clang/test/CodeGenCXX/ubsan-new-checks.cpp +++ b/clang/test/CodeGenCXX/ubsan-new-checks.cpp @@ -75,7 +75,7 @@ S3 *func_07() { // CHECK-LABEL: define {{.*}} @_Z7func_07v // CHECK: and i64 %{{.*}}, 31, !nosanitize // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize - // CHECK: and i64 %{{.*}}, 3, !nosanitize + // CHECK: and i64 %{{.*}}, 15, !nosanitize // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize // CHECK: ret ptr return new S3; @@ -85,7 +85,7 @@ S3 *func_08() { // CHECK-LABEL: define {{.*}} @_Z7func_08v // CHECK: and i64 %{{.*}}, 31, !nosanitize // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize - // CHECK: and i64 %{{.*}}, 3, !nosanitize + // CHECK: and i64 %{{.*}}, 15, !nosanitize // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize // CHECK: ret ptr return new S3[10]; diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp index 4642126ab74c4..96e1939ea4363 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/minimum-alignment.cpp @@ -1,7 +1,7 @@ -// RUN: %clangxx %gmlt -fsanitize=alignment %s -o %t +// RUN: %clangxx %gmlt -std=c++17 -m64 -fsanitize=alignment %s -o %t // RUN: %run %t 2>&1 | FileCheck %s -// UNSUPPORTED: i386 +// UNSUPPORTED: i386, i686 // UNSUPPORTED: armv7l // These sanitizers already overload the new operator so won't compile this test @@ -10,6 +10,7 @@ #include <cassert> #include <cstdlib> +#include <cstddef> void *operator new(std::size_t count) { constexpr const size_t offset = 8; diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp index 4b0b2b5923c6f..74d5c9a35754c 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp @@ -101,7 +101,7 @@ int main(int, char **argv) { return s->f() && 0; case 'n': - // CHECK-NEW: misaligned.cpp:[[@LINE+4]]{{(:21)?}}: runtime error: constructor call with pointer from operator new on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires target minimum assumed 4 byte alignment + // CHECK-NEW: misaligned.cpp:[[@LINE+4]]{{(:21)?}}: runtime error: constructor call{{( with pointer from operator new)?}} on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires {{(4|(target minimum assumed (8|(16))))}} byte alignment // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-NEW-NEXT: {{^ \^}} >From 10b77f37678a3d333b0e324b2118426eb5d2908f Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Thu, 6 Nov 2025 14:09:10 +0000 Subject: [PATCH 7/7] Add escape case if alignment is 1 --- clang/lib/CodeGen/CGExprCXX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 9acf3a1817c82..73edf245353b2 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1762,7 +1762,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { unsigned DefaultTargetAlignment = TI.getNewAlign() / TI.getCharWidth(); if (SanOpts.has(SanitizerKind::Alignment) && (DefaultTargetAlignment > - CGM.getContext().getTypeAlignInChars(allocType).getQuantity())){ + CGM.getContext().getTypeAlignInChars(allocType).getQuantity()) && + !result.getAlignment().isOne()) { checkKind = CodeGenFunction::TCK_ConstructorCallMinimumAlign; checkAlignment = CharUnits::fromQuantity(DefaultTargetAlignment); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
