https://github.com/vasu-the-sharma updated https://github.com/llvm/llvm-project/pull/164548
>From 856ac3b4110d79e57bfef9fed52c00a989683083 Mon Sep 17 00:00:00 2001 From: Vasu Sharma <[email protected]> Date: Wed, 22 Oct 2025 10:04:59 +0530 Subject: [PATCH 1/7] add null and aligment checks for aggregates --- clang/lib/CodeGen/CGExprAgg.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index eee397f1f3d19..de6d80a273dbd 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -2249,6 +2249,21 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty, bool isVolatile) { assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex"); + if (SanOpts.hasOneOf(SanitizerKind::Null | SanitizerKind::Alignment)) { + Address SrcAddr = Src.getAddress(); + Address DestAddr = Dest.getAddress(); + + // Check source pointer for null and alignment violations + EmitTypeCheck(TCK_Load, SourceLocation(), + SrcAddr.emitRawPointer(*this), Ty, SrcAddr.getAlignment(), + SanitizerSet()); + + // Check destination pointer for null and alignment violations + EmitTypeCheck(TCK_Store, SourceLocation(), + DestAddr.emitRawPointer(*this), Ty, DestAddr.getAlignment(), + SanitizerSet()); + } + Address DestPtr = Dest.getAddress(); Address SrcPtr = Src.getAddress(); >From 4b7ff1ed2976e27b82b0ce660d47749add49f817 Mon Sep 17 00:00:00 2001 From: Vasu Sharma <[email protected]> Date: Wed, 22 Oct 2025 16:15:22 +0530 Subject: [PATCH 2/7] Add null and alignment checks for aggregate copy operation --- clang/lib/CodeGen/CGExprAgg.cpp | 3 + .../Misc/Posix/aggregate_null_alignment.cpp | 79 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index de6d80a273dbd..2e5456233c711 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -2249,6 +2249,9 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty, bool isVolatile) { assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex"); + // Sanitizer checks to verify source and destination pointers are + // non-null and properly aligned before copying. + // Without these checks, undefined behavior from invalid pointers goes undetected. if (SanOpts.hasOneOf(SanitizerKind::Null | SanitizerKind::Alignment)) { Address SrcAddr = Src.getAddress(); Address DestAddr = Dest.getAddress(); diff --git a/compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp b/compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp new file mode 100644 index 0000000000000..de62c3d5a4df7 --- /dev/null +++ b/compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp @@ -0,0 +1,79 @@ +// RUN: %clangxx -fsanitize=alignment,null -O0 %s -o %t && %run %t +// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_NULL_SRC %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-SRC +// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_NULL_DEST %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-DEST +// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_MISALIGN_SRC %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ALIGN-SRC +// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_MISALIGN_DEST %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ALIGN-DEST + +// Tests for null pointer and alignment checks in aggregate copy operations. +// This validates the sanitizer checks added to EmitAggregateCopy for both +// source and destination pointers with null and alignment violations. + +#include <stdlib.h> +#include <string.h> + +struct alignas(16) AlignedStruct { + int a; + int b; + int c; + int d; +}; + +struct NormalStruct { + int x; + int y; + int z; +}; + +void test_null_src() { + AlignedStruct dest; + AlignedStruct *src = nullptr; + // CHECK-NULL-SRC: runtime error: load of null pointer of type 'AlignedStruct' + dest = *src; +} + +void test_null_dest() { + AlignedStruct src = {1, 2, 3, 4}; + AlignedStruct *dest = nullptr; + // CHECK-NULL-DEST: runtime error: store to null pointer of type 'AlignedStruct' + *dest = src; +} + +void test_misaligned_src() { + char buffer[sizeof(AlignedStruct) + 16]; + // Create a misaligned pointer (not 16-byte aligned) + AlignedStruct *src = (AlignedStruct *)(buffer + 1); + AlignedStruct dest; + // CHECK-ALIGN-SRC: runtime error: load of misaligned address {{0x[0-9a-f]+}} for type 'AlignedStruct', which requires 16 byte alignment + dest = *src; +} + +void test_misaligned_dest() { + AlignedStruct src = {1, 2, 3, 4}; + char buffer[sizeof(AlignedStruct) + 16]; + // Create a misaligned pointer (not 16-byte aligned) + AlignedStruct *dest = (AlignedStruct *)(buffer + 1); + // CHECK-ALIGN-DEST: runtime error: store to misaligned address {{0x[0-9a-f]+}} for type 'AlignedStruct', which requires 16 byte alignment + *dest = src; +} + +void test_normal_copy() { + // This should work fine - properly aligned, non-null pointers + AlignedStruct src = {1, 2, 3, 4}; + AlignedStruct dest; + dest = src; +} + +int main() { +#ifdef TEST_NULL_SRC + test_null_src(); +#elif defined(TEST_NULL_DEST) + test_null_dest(); +#elif defined(TEST_MISALIGN_SRC) + test_misaligned_src(); +#elif defined(TEST_MISALIGN_DEST) + test_misaligned_dest(); +#else + test_normal_copy(); +#endif + return 0; +} >From 2bfcdf54cf04a300429112f40dcb699ac1ba8403 Mon Sep 17 00:00:00 2001 From: Vasu Sharma <[email protected]> Date: Tue, 28 Oct 2025 16:14:33 +0530 Subject: [PATCH 3/7] add codegen test --- clang/lib/CodeGen/CGExprAgg.cpp | 26 ++-- .../CodeGen/ubsan-aggregate-null-align.cpp | 134 ++++++++++++++++++ .../Misc/Posix/aggregate_null_alignment.cpp | 79 ----------- 3 files changed, 146 insertions(+), 93 deletions(-) create mode 100644 clang/test/CodeGen/ubsan-aggregate-null-align.cpp delete mode 100644 compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 2e5456233c711..cf215da08fbf1 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -2252,20 +2252,18 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty, // Sanitizer checks to verify source and destination pointers are // non-null and properly aligned before copying. // Without these checks, undefined behavior from invalid pointers goes undetected. - if (SanOpts.hasOneOf(SanitizerKind::Null | SanitizerKind::Alignment)) { - Address SrcAddr = Src.getAddress(); - Address DestAddr = Dest.getAddress(); - - // Check source pointer for null and alignment violations - EmitTypeCheck(TCK_Load, SourceLocation(), - SrcAddr.emitRawPointer(*this), Ty, SrcAddr.getAlignment(), - SanitizerSet()); - - // Check destination pointer for null and alignment violations - EmitTypeCheck(TCK_Store, SourceLocation(), - DestAddr.emitRawPointer(*this), Ty, DestAddr.getAlignment(), - SanitizerSet()); - } + Address SrcAddr = Src.getAddress(); + Address DestAddr = Dest.getAddress(); + + // Check source pointer for null and alignment violations + EmitTypeCheck(TCK_Load, SourceLocation(), + SrcAddr.emitRawPointer(*this), Ty, SrcAddr.getAlignment(), + SanitizerSet()); + + // Check destination pointer for null and alignment violations + EmitTypeCheck(TCK_Store, SourceLocation(), + DestAddr.emitRawPointer(*this), Ty, DestAddr.getAlignment(), + SanitizerSet()); Address DestPtr = Dest.getAddress(); Address SrcPtr = Src.getAddress(); diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.cpp b/clang/test/CodeGen/ubsan-aggregate-null-align.cpp new file mode 100644 index 0000000000000..d623ea0d1e701 --- /dev/null +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.cpp @@ -0,0 +1,134 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=alignment,null \ +// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-UBSAN +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK-NO-UBSAN + +// Test that EmitAggregateCopy emits null and alignment checks when sanitizers +// are enabled for aggregate copy operations with pointers. + +struct alignas(16) AlignedStruct { + int a; + int b; + int c; + int d; +}; + +struct NormalStruct { + int x; + int y; + int z; +}; + +// Stack-to-stack copies are optimized away (compiler knows they're valid) +// CHECK-UBSAN-LABEL: define {{.*}}void @_Z19test_aligned_structv() +// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z19test_aligned_structv() +void test_aligned_struct() { + AlignedStruct src = {1, 2, 3, 4}; + AlignedStruct dest; + + // CHECK-UBSAN: call void @llvm.memcpy + // CHECK-NO-UBSAN: call void @llvm.memcpy + + dest = src; +} + +// CHECK-UBSAN-LABEL: define {{.*}}void @_Z18test_normal_structv() +// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z18test_normal_structv() +void test_normal_struct() { + NormalStruct src = {10, 20, 30}; + NormalStruct dest; + + // CHECK-UBSAN: call void @llvm.memcpy + // CHECK-NO-UBSAN: call void @llvm.memcpy + + dest = src; +} + +// This is the key test - copying through pointers requires runtime checks +// CHECK-UBSAN-LABEL: define {{.*}}void @_Z19test_pointer_to_ptrP13AlignedStructS0_( +// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z19test_pointer_to_ptrP13AlignedStructS0_( +void test_pointer_to_ptr(AlignedStruct *src, AlignedStruct *dest) { + // CHECK-UBSAN: %[[SRC_LOAD:.*]] = load ptr, ptr %src.addr + // CHECK-UBSAN: %[[DEST_LOAD:.*]] = load ptr, ptr %dest.addr + + // Check source pointer is non-null and aligned + // CHECK-UBSAN: %[[SRC_NONNULL:.*]] = icmp ne ptr %[[SRC_LOAD]], null + // CHECK-UBSAN: %[[SRC_INT:.*]] = ptrtoint ptr %[[SRC_LOAD]] to i64 + // CHECK-UBSAN: %[[SRC_MASK:.*]] = and i64 %[[SRC_INT]], 15 + // CHECK-UBSAN: %[[SRC_ALIGNED:.*]] = icmp eq i64 %[[SRC_MASK]], 0 + // CHECK-UBSAN: %[[SRC_OK:.*]] = and i1 %[[SRC_NONNULL]], %[[SRC_ALIGNED]] + // CHECK-UBSAN: br i1 %[[SRC_OK]], label %cont, label %handler.type_mismatch + + // CHECK-UBSAN: handler.type_mismatch: + // CHECK-UBSAN: call void @__ubsan_handle_type_mismatch_v1_abort + // CHECK-UBSAN: unreachable + + // CHECK-UBSAN: cont: + // Check destination pointer is non-null and aligned + // CHECK-UBSAN: %[[DEST_NONNULL:.*]] = icmp ne ptr %[[DEST_LOAD]], null + // CHECK-UBSAN: %[[DEST_INT:.*]] = ptrtoint ptr %[[DEST_LOAD]] to i64 + // CHECK-UBSAN: %[[DEST_MASK:.*]] = and i64 %[[DEST_INT]], 15 + // CHECK-UBSAN: %[[DEST_ALIGNED:.*]] = icmp eq i64 %[[DEST_MASK]], 0 + // CHECK-UBSAN: %[[DEST_OK:.*]] = and i1 %[[DEST_NONNULL]], %[[DEST_ALIGNED]] + // CHECK-UBSAN: br i1 %[[DEST_OK]], label %cont{{.*}}, label %handler.type_mismatch + + // CHECK-UBSAN: handler.type_mismatch{{.*}}: + // CHECK-UBSAN: call void @__ubsan_handle_type_mismatch_v1_abort + // CHECK-UBSAN: unreachable + + // CHECK-UBSAN: cont{{.*}}: + // CHECK-UBSAN: call void @llvm.memcpy + + // Without sanitizers, no checks - just direct memcpy + // CHECK-NO-UBSAN-NOT: @__ubsan_handle + // CHECK-NO-UBSAN: call void @llvm.memcpy + + *dest = *src; +} + +// Array copies also need checks for non-constant indices +// CHECK-UBSAN-LABEL: define {{.*}}void @_Z15test_array_copyv() +// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z15test_array_copyv() +void test_array_copy() { + AlignedStruct src[3] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + AlignedStruct dest[3]; + + // First element - no checks needed (compiler knows it's aligned) + // CHECK-UBSAN: %arrayidx = getelementptr inbounds [3 x %struct.AlignedStruct] + // CHECK-UBSAN: %arrayidx1 = getelementptr inbounds [3 x %struct.AlignedStruct] + // CHECK-UBSAN: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %arrayidx1, ptr align 16 %arrayidx, i64 16, i1 false) + dest[0] = src[0]; + + // Second element - needs runtime checks + // CHECK-UBSAN: %arrayidx{{.*}} = getelementptr inbounds [3 x %struct.AlignedStruct] + // CHECK-UBSAN: %arrayidx{{.*}} = getelementptr inbounds [3 x %struct.AlignedStruct] + // CHECK-UBSAN: icmp ne ptr %arrayidx{{.*}}, null + // CHECK-UBSAN: ptrtoint ptr %arrayidx{{.*}} to i64 + // CHECK-UBSAN: and i64 %{{.*}}, 15 + // CHECK-UBSAN: icmp eq i64 %{{.*}}, 0 + // CHECK-UBSAN: br i1 %{{.*}}, label %cont, label %handler.type_mismatch + // CHECK-UBSAN: handler.type_mismatch: + // CHECK-UBSAN: call void @__ubsan_handle_type_mismatch_v1_abort + dest[1] = src[1]; + + // Third element - also needs checks + // CHECK-UBSAN: icmp ne ptr %arrayidx{{.*}}, null + // CHECK-UBSAN: call void @__ubsan_handle_type_mismatch_v1_abort + dest[2] = src[2]; + + // Without sanitizers, no checks + // CHECK-NO-UBSAN-NOT: @__ubsan_handle +} + +// Test with normal struct through pointers +// CHECK-UBSAN-LABEL: define {{.*}}void @_Z23test_normal_struct_ptrsP12NormalStructS0_ +// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z23test_normal_struct_ptrsP12NormalStructS0_ +void test_normal_struct_ptrs(NormalStruct *src, NormalStruct *dest) { + // Should still check for null even with normal alignment + // CHECK-UBSAN: icmp ne ptr %{{.*}}, null + // CHECK-UBSAN: call void @__ubsan_handle_type_mismatch_v1_abort + + // CHECK-NO-UBSAN-NOT: @__ubsan_handle + + *dest = *src; +} diff --git a/compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp b/compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp deleted file mode 100644 index de62c3d5a4df7..0000000000000 --- a/compiler-rt/test/ubsan/TestCases/Misc/Posix/aggregate_null_alignment.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// RUN: %clangxx -fsanitize=alignment,null -O0 %s -o %t && %run %t -// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_NULL_SRC %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-SRC -// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_NULL_DEST %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-DEST -// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_MISALIGN_SRC %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ALIGN-SRC -// RUN: %clangxx -fsanitize=alignment,null -O0 -DTEST_MISALIGN_DEST %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ALIGN-DEST - -// Tests for null pointer and alignment checks in aggregate copy operations. -// This validates the sanitizer checks added to EmitAggregateCopy for both -// source and destination pointers with null and alignment violations. - -#include <stdlib.h> -#include <string.h> - -struct alignas(16) AlignedStruct { - int a; - int b; - int c; - int d; -}; - -struct NormalStruct { - int x; - int y; - int z; -}; - -void test_null_src() { - AlignedStruct dest; - AlignedStruct *src = nullptr; - // CHECK-NULL-SRC: runtime error: load of null pointer of type 'AlignedStruct' - dest = *src; -} - -void test_null_dest() { - AlignedStruct src = {1, 2, 3, 4}; - AlignedStruct *dest = nullptr; - // CHECK-NULL-DEST: runtime error: store to null pointer of type 'AlignedStruct' - *dest = src; -} - -void test_misaligned_src() { - char buffer[sizeof(AlignedStruct) + 16]; - // Create a misaligned pointer (not 16-byte aligned) - AlignedStruct *src = (AlignedStruct *)(buffer + 1); - AlignedStruct dest; - // CHECK-ALIGN-SRC: runtime error: load of misaligned address {{0x[0-9a-f]+}} for type 'AlignedStruct', which requires 16 byte alignment - dest = *src; -} - -void test_misaligned_dest() { - AlignedStruct src = {1, 2, 3, 4}; - char buffer[sizeof(AlignedStruct) + 16]; - // Create a misaligned pointer (not 16-byte aligned) - AlignedStruct *dest = (AlignedStruct *)(buffer + 1); - // CHECK-ALIGN-DEST: runtime error: store to misaligned address {{0x[0-9a-f]+}} for type 'AlignedStruct', which requires 16 byte alignment - *dest = src; -} - -void test_normal_copy() { - // This should work fine - properly aligned, non-null pointers - AlignedStruct src = {1, 2, 3, 4}; - AlignedStruct dest; - dest = src; -} - -int main() { -#ifdef TEST_NULL_SRC - test_null_src(); -#elif defined(TEST_NULL_DEST) - test_null_dest(); -#elif defined(TEST_MISALIGN_SRC) - test_misaligned_src(); -#elif defined(TEST_MISALIGN_DEST) - test_misaligned_dest(); -#else - test_normal_copy(); -#endif - return 0; -} >From c0284811a286f2d50300c31780a68704b084732f Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Fri, 14 Nov 2025 18:34:24 -0500 Subject: [PATCH 4/7] Call `EmitCheckedLValue` from additional `AggExprEmitter` functions ... instead of calling `EmitTypeCheck` directly from `EmitAggregateCopy` (at which point contextual information, including the source location, is already lost). --- clang/lib/CodeGen/CGExprAgg.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index cf215da08fbf1..b828bd39a8691 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -254,7 +254,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { /// represents a value lvalue, this method emits the address of the lvalue, /// then loads the result into DestPtr. void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { - LValue LV = CGF.EmitLValue(E); + LValue LV = CGF.EmitCheckedLValue(E, CodeGenFunction::TCK_Load); // If the type of the l-value is atomic, then do an atomic load. if (LV.getType()->isAtomicType() || CGF.LValueIsSuitableForInlineAtomic(LV)) { @@ -1328,7 +1328,7 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { return; } - LValue LHS = CGF.EmitLValue(E->getLHS()); + LValue LHS = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); // If we have an atomic type, evaluate into the destination and then // do an atomic copy. @@ -2249,22 +2249,6 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty, bool isVolatile) { assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex"); - // Sanitizer checks to verify source and destination pointers are - // non-null and properly aligned before copying. - // Without these checks, undefined behavior from invalid pointers goes undetected. - Address SrcAddr = Src.getAddress(); - Address DestAddr = Dest.getAddress(); - - // Check source pointer for null and alignment violations - EmitTypeCheck(TCK_Load, SourceLocation(), - SrcAddr.emitRawPointer(*this), Ty, SrcAddr.getAlignment(), - SanitizerSet()); - - // Check destination pointer for null and alignment violations - EmitTypeCheck(TCK_Store, SourceLocation(), - DestAddr.emitRawPointer(*this), Ty, DestAddr.getAlignment(), - SanitizerSet()); - Address DestPtr = Dest.getAddress(); Address SrcPtr = Src.getAddress(); >From 4d21a0e7c33215c5363ebd5e269f1e9ad83aea91 Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Fri, 14 Nov 2025 22:58:38 -0500 Subject: [PATCH 5/7] Update test to compile as either C or C++ --- ...align.cpp => ubsan-aggregate-null-align.c} | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) rename clang/test/CodeGen/{ubsan-aggregate-null-align.cpp => ubsan-aggregate-null-align.c} (80%) diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.cpp b/clang/test/CodeGen/ubsan-aggregate-null-align.c similarity index 80% rename from clang/test/CodeGen/ubsan-aggregate-null-align.cpp rename to clang/test/CodeGen/ubsan-aggregate-null-align.c index d623ea0d1e701..ddf4c89d2d050 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.cpp +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -1,27 +1,36 @@ // RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=alignment,null \ -// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-UBSAN -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - \ -// RUN: | FileCheck %s --check-prefix=CHECK-NO-UBSAN +// RUN: -emit-llvm -std=c23 %s -o - \ +// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-UBSAN +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -std=c23 %s -o - \ +// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-NO-UBSAN +// +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=alignment,null \ +// RUN: -emit-llvm -xc++ %s -o - \ +// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-UBSAN +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -xc++ %s -o - \ +// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-NO-UBSAN // Test that EmitAggregateCopy emits null and alignment checks when sanitizers // are enabled for aggregate copy operations with pointers. -struct alignas(16) AlignedStruct { - int a; +typedef struct AlignedStruct { + alignas(16) int a; int b; int c; int d; -}; +} AlignedStruct; -struct NormalStruct { +typedef struct NormalStruct { int x; int y; int z; -}; +} NormalStruct; +#if __cplusplus +extern "C" { +#endif // Stack-to-stack copies are optimized away (compiler knows they're valid) -// CHECK-UBSAN-LABEL: define {{.*}}void @_Z19test_aligned_structv() -// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z19test_aligned_structv() +// CHECK-LABEL: define {{.*}}void @test_aligned_struct() void test_aligned_struct() { AlignedStruct src = {1, 2, 3, 4}; AlignedStruct dest; @@ -32,8 +41,7 @@ void test_aligned_struct() { dest = src; } -// CHECK-UBSAN-LABEL: define {{.*}}void @_Z18test_normal_structv() -// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z18test_normal_structv() +// CHECK-LABEL: define {{.*}}void @test_normal_struct() void test_normal_struct() { NormalStruct src = {10, 20, 30}; NormalStruct dest; @@ -45,8 +53,7 @@ void test_normal_struct() { } // This is the key test - copying through pointers requires runtime checks -// CHECK-UBSAN-LABEL: define {{.*}}void @_Z19test_pointer_to_ptrP13AlignedStructS0_( -// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z19test_pointer_to_ptrP13AlignedStructS0_( +// CHECK-LABEL: define {{.*}}void @test_pointer_to_ptr( void test_pointer_to_ptr(AlignedStruct *src, AlignedStruct *dest) { // CHECK-UBSAN: %[[SRC_LOAD:.*]] = load ptr, ptr %src.addr // CHECK-UBSAN: %[[DEST_LOAD:.*]] = load ptr, ptr %dest.addr @@ -87,8 +94,7 @@ void test_pointer_to_ptr(AlignedStruct *src, AlignedStruct *dest) { } // Array copies also need checks for non-constant indices -// CHECK-UBSAN-LABEL: define {{.*}}void @_Z15test_array_copyv() -// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z15test_array_copyv() +// CHECK-LABEL: define {{.*}}void @test_array_copy() void test_array_copy() { AlignedStruct src[3] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; AlignedStruct dest[3]; @@ -121,8 +127,7 @@ void test_array_copy() { } // Test with normal struct through pointers -// CHECK-UBSAN-LABEL: define {{.*}}void @_Z23test_normal_struct_ptrsP12NormalStructS0_ -// CHECK-NO-UBSAN-LABEL: define {{.*}}void @_Z23test_normal_struct_ptrsP12NormalStructS0_ +// CHECK-LABEL: define {{.*}}void @test_normal_struct_ptrs( void test_normal_struct_ptrs(NormalStruct *src, NormalStruct *dest) { // Should still check for null even with normal alignment // CHECK-UBSAN: icmp ne ptr %{{.*}}, null @@ -132,3 +137,6 @@ void test_normal_struct_ptrs(NormalStruct *src, NormalStruct *dest) { *dest = *src; } +#if __cplusplus +} +#endif >From 1e608963ffd6bdc1c3b9a874398589dce77bc3cf Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Sat, 15 Nov 2025 00:36:02 -0500 Subject: [PATCH 6/7] Use EmitCheckedLValue for C++ trivial assignment operator calls --- clang/lib/CodeGen/CGExprCXX.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 14d8db32bafc6..c130561a0ac78 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -267,7 +267,7 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(CE)) { if (OCE->isAssignmentOp()) { if (TrivialAssignment) { - TrivialAssignmentRHS = EmitLValue(CE->getArg(1)); + TrivialAssignmentRHS = EmitCheckedLValue(CE->getArg(1), TCK_Load); } else { RtlArgs = &RtlArgStorage; EmitCallArgs(*RtlArgs, MD->getType()->castAs<FunctionProtoType>(), @@ -277,22 +277,26 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( } } - LValue This; - if (IsArrow) { - LValueBaseInfo BaseInfo; - TBAAAccessInfo TBAAInfo; - Address ThisValue = EmitPointerWithAlignment(Base, &BaseInfo, &TBAAInfo); - This = MakeAddrLValue(ThisValue, Base->getType()->getPointeeType(), - BaseInfo, TBAAInfo); - } else { - This = EmitLValue(Base); - } + auto getLValueForThis = [this, IsArrow, + Base](bool EmitCheckedForStore = false) { + if (IsArrow) { + LValueBaseInfo BaseInfo; + TBAAAccessInfo TBAAInfo; + Address ThisValue = EmitPointerWithAlignment(Base, &BaseInfo, &TBAAInfo); + return MakeAddrLValue(ThisValue, Base->getType()->getPointeeType(), + BaseInfo, TBAAInfo); + } + if (EmitCheckedForStore) + return EmitCheckedLValue(Base, TCK_Store); + return EmitLValue(Base); + }; if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(MD)) { // This is the MSVC p->Ctor::Ctor(...) extension. We assume that's // constructing a new complete object of type Ctor. assert(!RtlArgs); assert(ReturnValue.isNull() && "Constructor shouldn't have return value"); + LValue This = getLValueForThis(); CallArgList Args; commonEmitCXXMemberOrOperatorCall( *this, {Ctor, Ctor_Complete}, This.getPointer(*this), @@ -313,12 +317,14 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( if (TrivialAssignment) { // We don't like to generate the trivial copy/move assignment operator // when it isn't necessary; just produce the proper effect here. + LValue This = getLValueForThis(/*EmitCheckedForStore=*/true); + // It's important that we use the result of EmitLValue here rather than // emitting call arguments, in order to preserve TBAA information from // the RHS. LValue RHS = isa<CXXOperatorCallExpr>(CE) ? TrivialAssignmentRHS - : EmitLValue(*CE->arg_begin()); + : EmitCheckedLValue(*CE->arg_begin(), TCK_Load); EmitAggregateAssign(This, RHS, CE->getType()); return RValue::get(This.getPointer(*this)); } @@ -357,6 +363,7 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( SkippedChecks.set(SanitizerKind::Null, true); } + LValue This = getLValueForThis(); if (sanitizePerformTypeCheck()) EmitTypeCheck(CodeGenFunction::TCK_MemberCall, CallLoc, This.emitRawPointer(*this), >From bc7f979bcb205b82d43d99fd940889f3d8801ca4 Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Sat, 15 Nov 2025 01:08:35 -0500 Subject: [PATCH 7/7] (partial) Use EmitCheckedLValue for C++ trivial copy/move ctor calls --- clang/lib/CodeGen/CGClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index f782b0cd17da4..9b16961cf3f7b 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2217,7 +2217,7 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, assert(E->getNumArgs() == 1 && "unexpected argcount for trivial ctor"); const Expr *Arg = E->getArg(0); - LValue Src = EmitLValue(Arg); + LValue Src = EmitCheckedLValue(Arg, TCK_Load); CanQualType DestTy = getContext().getCanonicalTagType(D->getParent()); LValue Dest = MakeAddrLValue(This, DestTy); EmitAggregateCopyCtor(Dest, Src, ThisAVS.mayOverlap()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
