https://github.com/gbMattN updated https://github.com/llvm/llvm-project/pull/152532
>From 47142640181a1c882429a2ba7b83d87d18cfd7d1 Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Fri, 17 Oct 2025 13:49:55 +0100 Subject: [PATCH] [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 c52526c89f171..a7f5f6c59e8fd 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; @@ -1736,6 +1740,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. @@ -1743,7 +1762,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 f0565c1de04c4..7b0810c047fec 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3292,7 +3292,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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
