https://github.com/vasu-the-sharma updated https://github.com/llvm/llvm-project/pull/190739
>From 07d7cae1b4e663ad53860ea6fe1eda66f74c41ae Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Tue, 7 Apr 2026 03:01:00 -0400 Subject: [PATCH 1/8] add null, alignment, and array-bounds checks --- clang/lib/CodeGen/CGClass.cpp | 2 +- clang/lib/CodeGen/CGExprAgg.cpp | 2 +- clang/lib/CodeGen/CGExprCXX.cpp | 2 +- .../test/CodeGen/ubsan-aggregate-null-align.c | 90 +++++++++---------- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index c0482fb13ec79..de11e8bca43f1 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2282,7 +2282,7 @@ void CodeGenFunction::EmitCXXConstructorCall( 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()); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 3a4291719da74..2d6e183e85aee 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -246,7 +246,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)) { diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 82300c3ede183..e98015cb8fabb 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>(), diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c b/clang/test/CodeGen/ubsan-aggregate-null-align.c index 1e2b60d7bde14..310ec0b8bf86d 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.c +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -1,10 +1,9 @@ // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 %s -o %t.c.ll && FileCheck %s --check-prefixes=C,SHARED < %t.c.ll // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c++17 -x c++ -O0 %s -o %t.cxx.ll && FileCheck %s --check-prefixes=CXX,SHARED < %t.cxx.ll -// Precommit test for null, alignment, and array-bounds checks on aggregates. -// This test documents current behavior: memcpy is called but source operand is not checked -// for null/alignment (unlike scalar types). Array bounds checks exist for local -// arrays but not for past-the-end pointer accesses via parameters. +// Test for null, alignment, and array-bounds checks on aggregates. +// Verifies that the source operand is checked for null/alignment before memcpy. +// Array bounds checks exist for local arrays. struct Small { int x; }; struct Container { struct Small inner; }; @@ -18,8 +17,8 @@ extern "C" { // SHARED-LABEL: define {{[^@]*}}@test_assign_plain_arr_idx // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_assign_plain_arr_idx(struct Small *dest, struct Small arr[4]) { *dest = arr[0]; @@ -28,8 +27,8 @@ __attribute__((noinline)) void test_assign_plain_arr_idx(struct Small *dest, str // SHARED-LABEL: define {{[^@]*}}@test_init_plain_arr_idx // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_plain_arr_idx(struct Small arr[4]) { struct Small a = arr[0]; @@ -38,15 +37,15 @@ __attribute__((noinline)) void test_init_plain_arr_idx(struct Small arr[4]) { // SHARED-LABEL: define {{[^@]*}}@test_init_list_plain_arr_idx // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_list_plain_arr_idx(struct Small arr[4]) { struct Small a[] = {arr[0]}; } // SHARED-LABEL: define {{[^@]*}}@test_nested_member_plain_arr_idx -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort +// SHARED: __ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64 __attribute__((noinline)) void test_nested_member_plain_arr_idx(struct Container *c, struct Small arr[4]) { c->inner = arr[0]; @@ -56,8 +55,8 @@ __attribute__((noinline)) void test_nested_member_plain_arr_idx(struct Container // SHARED-LABEL: define {{[^@]*}}@test_assign_plain_deref_ptr // SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_assign_plain_deref_ptr(struct Small *dest, struct Small *ap) { *dest = *ap; @@ -65,8 +64,8 @@ __attribute__((noinline)) void test_assign_plain_deref_ptr(struct Small *dest, s // SHARED-LABEL: define {{[^@]*}}@test_init_plain_deref_ptr // SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_plain_deref_ptr(struct Small *ap) { struct Small a = *ap; @@ -74,15 +73,15 @@ __attribute__((noinline)) void test_init_plain_deref_ptr(struct Small *ap) { // SHARED-LABEL: define {{[^@]*}}@test_init_list_plain_deref_ptr // SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_list_plain_deref_ptr(struct Small *ap) { struct Small a[] = {*ap}; } // SHARED-LABEL: define {{[^@]*}}@test_nested_member_plain_deref_ptr -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort +// SHARED: __ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64 __attribute__((noinline)) void test_nested_member_plain_deref_ptr(struct Container *c, struct Small *ap) { c->inner = *ap; @@ -91,7 +90,8 @@ __attribute__((noinline)) void test_nested_member_plain_deref_ptr(struct Contain // Misaligned aggregate access // SHARED-LABEL: define {{[^@]*}}@test_misaligned_access -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort +// SHARED: icmp ne ptr +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy __attribute__((noinline)) void test_misaligned_access(struct Small *dest, char *buf) { struct Small *p = (struct Small *)(buf + 1); // Misaligned @@ -101,8 +101,8 @@ __attribute__((noinline)) void test_misaligned_access(struct Small *dest, char * // Array bounds: out-of-bounds on local array // SHARED-LABEL: define {{[^@]*}}@test_local_array_oob -// SHARED: call void @__ubsan_handle_out_of_bounds_abort -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort +// SHARED: call void @__ubsan_handle_out_of_bounds +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64 __attribute__((noinline)) void test_local_array_oob(struct Small *dest) { struct Small arr[4]; @@ -114,9 +114,9 @@ __attribute__((noinline)) void test_local_array_oob(struct Small *dest) { // SHARED-LABEL: define {{[^@]*}}@test_past_the_end_arr_idx // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// SHARED-NOT: call void @__ubsan_handle_out_of_bounds_abort -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED-NOT: __ubsan_handle_out_of_bounds +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_past_the_end_arr_idx(struct Small *dest, struct Small arr[4]) { *dest = arr[4]; @@ -125,9 +125,9 @@ __attribute__((noinline)) void test_past_the_end_arr_idx(struct Small *dest, str // SHARED-LABEL: define {{[^@]*}}@test_past_the_end_init // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// SHARED-NOT: call void @__ubsan_handle_out_of_bounds_abort -// SHARED-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// SHARED-NOT: icmp ne ptr [[SRC]], null +// SHARED-NOT: __ubsan_handle_out_of_bounds +// SHARED: icmp ne ptr [[SRC]], null +// SHARED: call void @__ubsan_handle_type_mismatch // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_past_the_end_init(struct Small arr[4]) { struct Small a = arr[4]; @@ -143,8 +143,8 @@ __attribute__((noinline)) void test_past_the_end_init(struct Small arr[4]) { // C-LABEL: define {{[^@]*}}@test_assign_atomic_deref_ptr // C: [[SRC:%.*]] = load ptr, ptr %ap.addr -// C-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// C-NOT: icmp ne ptr [[SRC]], null +// C: icmp ne ptr [[SRC]], null +// C: call void @__ubsan_handle_type_mismatch // C: load atomic i32, ptr [[SRC]] seq_cst __attribute__((noinline)) void test_assign_atomic_deref_ptr(struct Small *dest, _Atomic(struct Small) *ap) { *dest = *ap; @@ -161,8 +161,8 @@ extern "C" { // CXX-LABEL: define {{[^@]*}}@test_cxx_init_direct_plain_arr_idx // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_direct_plain_arr_idx(struct Small arr[4]) { struct Small a(arr[0]); @@ -171,8 +171,8 @@ __attribute__((noinline)) void test_cxx_init_direct_plain_arr_idx(struct Small a // CXX-LABEL: define {{[^@]*}}@test_cxx_init_brace_plain_arr_idx // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_brace_plain_arr_idx(struct Small arr[4]) { struct Small a{arr[0]}; @@ -180,8 +180,8 @@ __attribute__((noinline)) void test_cxx_init_brace_plain_arr_idx(struct Small ar // CXX-LABEL: define {{[^@]*}}@test_cxx_init_direct_plain_deref_ptr // CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_direct_plain_deref_ptr(struct Small *ap) { struct Small a(*ap); @@ -189,8 +189,8 @@ __attribute__((noinline)) void test_cxx_init_direct_plain_deref_ptr(struct Small // CXX-LABEL: define {{[^@]*}}@test_cxx_init_brace_plain_deref_ptr // CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_brace_plain_deref_ptr(struct Small *ap) { struct Small a{*ap}; @@ -198,8 +198,8 @@ __attribute__((noinline)) void test_cxx_init_brace_plain_deref_ptr(struct Small // CXX-LABEL: define {{[^@]*}}@test_cxx_new_direct_plain_deref_ptr // CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_new_direct_plain_deref_ptr(struct Small *ap) { struct Small *a = new struct Small(*ap); @@ -211,9 +211,9 @@ __attribute__((noinline)) void test_cxx_new_direct_plain_deref_ptr(struct Small // CXX-LABEL: define {{[^@]*}}@test_cxx_past_the_end_direct // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// CXX-NOT: call void @__ubsan_handle_out_of_bounds_abort -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX-NOT: __ubsan_handle_out_of_bounds +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_past_the_end_direct(struct Small arr[4]) { struct Small a(arr[4]); @@ -222,9 +222,9 @@ __attribute__((noinline)) void test_cxx_past_the_end_direct(struct Small arr[4]) // CXX-LABEL: define {{[^@]*}}@test_cxx_past_the_end_brace // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// CXX-NOT: call void @__ubsan_handle_out_of_bounds_abort -// CXX-NOT: call void @__ubsan_handle_type_mismatch_v1_abort -// CXX-NOT: icmp ne ptr [[SRC]], null +// CXX-NOT: __ubsan_handle_out_of_bounds +// CXX: icmp ne ptr [[SRC]], null +// CXX: call void @__ubsan_handle_type_mismatch // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_past_the_end_brace(struct Small arr[4]) { struct Small a{arr[4]}; >From cdd05859ebba55636d5042ac7b898529998d9405 Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Tue, 7 Apr 2026 08:22:55 -0400 Subject: [PATCH 2/8] update ubsan-new-checks.cpp to expect new alignment check --- clang/test/CodeGenCXX/ubsan-new-checks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/test/CodeGenCXX/ubsan-new-checks.cpp b/clang/test/CodeGenCXX/ubsan-new-checks.cpp index 60edd323648ab..adc54ccbb00b7 100644 --- a/clang/test/CodeGenCXX/ubsan-new-checks.cpp +++ b/clang/test/CodeGenCXX/ubsan-new-checks.cpp @@ -140,7 +140,8 @@ S5 *func_15(const S5 *ptr) { // CHECK-LABEL: define {{.*}} @_Z7func_15PK2S5 // CHECK: and i64 %{{.*}}, 31, !nosanitize // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize - // CHECK-NOT: and i64 + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize // CHECK: ret ptr return new S5(*ptr); } >From a5f9291e9739bd64634cbca16b65ca291ac90ef6 Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Thu, 9 Apr 2026 07:58:47 -0400 Subject: [PATCH 3/8] update FileCheck patterns and comments --- .../test/CodeGen/ubsan-aggregate-null-align.c | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c b/clang/test/CodeGen/ubsan-aggregate-null-align.c index 310ec0b8bf86d..c652a3729fe95 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.c +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -18,7 +18,7 @@ extern "C" { // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_assign_plain_arr_idx(struct Small *dest, struct Small arr[4]) { *dest = arr[0]; @@ -28,7 +28,7 @@ __attribute__((noinline)) void test_assign_plain_arr_idx(struct Small *dest, str // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_plain_arr_idx(struct Small arr[4]) { struct Small a = arr[0]; @@ -38,14 +38,16 @@ __attribute__((noinline)) void test_init_plain_arr_idx(struct Small arr[4]) { // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_list_plain_arr_idx(struct Small arr[4]) { struct Small a[] = {arr[0]}; } +// Two ubsan calls: one for destination (c), one for source (arr[0]) // SHARED-LABEL: define {{[^@]*}}@test_nested_member_plain_arr_idx -// SHARED: __ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64 __attribute__((noinline)) void test_nested_member_plain_arr_idx(struct Container *c, struct Small arr[4]) { c->inner = arr[0]; @@ -56,7 +58,7 @@ __attribute__((noinline)) void test_nested_member_plain_arr_idx(struct Container // SHARED-LABEL: define {{[^@]*}}@test_assign_plain_deref_ptr // SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_assign_plain_deref_ptr(struct Small *dest, struct Small *ap) { *dest = *ap; @@ -65,7 +67,7 @@ __attribute__((noinline)) void test_assign_plain_deref_ptr(struct Small *dest, s // SHARED-LABEL: define {{[^@]*}}@test_init_plain_deref_ptr // SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_plain_deref_ptr(struct Small *ap) { struct Small a = *ap; @@ -74,14 +76,16 @@ __attribute__((noinline)) void test_init_plain_deref_ptr(struct Small *ap) { // SHARED-LABEL: define {{[^@]*}}@test_init_list_plain_deref_ptr // SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_init_list_plain_deref_ptr(struct Small *ap) { struct Small a[] = {*ap}; } +// Two ubsan calls: one for destination (c), one for source (*ap) // SHARED-LABEL: define {{[^@]*}}@test_nested_member_plain_deref_ptr -// SHARED: __ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64 __attribute__((noinline)) void test_nested_member_plain_deref_ptr(struct Container *c, struct Small *ap) { c->inner = *ap; @@ -91,7 +95,7 @@ __attribute__((noinline)) void test_nested_member_plain_deref_ptr(struct Contain // SHARED-LABEL: define {{[^@]*}}@test_misaligned_access // SHARED: icmp ne ptr -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy __attribute__((noinline)) void test_misaligned_access(struct Small *dest, char *buf) { struct Small *p = (struct Small *)(buf + 1); // Misaligned @@ -101,8 +105,8 @@ __attribute__((noinline)) void test_misaligned_access(struct Small *dest, char * // Array bounds: out-of-bounds on local array // SHARED-LABEL: define {{[^@]*}}@test_local_array_oob -// SHARED: call void @__ubsan_handle_out_of_bounds -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_out_of_bounds_abort( +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64 __attribute__((noinline)) void test_local_array_oob(struct Small *dest) { struct Small arr[4]; @@ -114,9 +118,9 @@ __attribute__((noinline)) void test_local_array_oob(struct Small *dest) { // SHARED-LABEL: define {{[^@]*}}@test_past_the_end_arr_idx // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// SHARED-NOT: __ubsan_handle_out_of_bounds +// SHARED-NOT: call void @__ubsan_handle_out_of_bounds_abort( // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_past_the_end_arr_idx(struct Small *dest, struct Small arr[4]) { *dest = arr[4]; @@ -125,9 +129,9 @@ __attribute__((noinline)) void test_past_the_end_arr_idx(struct Small *dest, str // SHARED-LABEL: define {{[^@]*}}@test_past_the_end_init // SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr // SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// SHARED-NOT: __ubsan_handle_out_of_bounds +// SHARED-NOT: call void @__ubsan_handle_out_of_bounds_abort( // SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch +// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( // SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_past_the_end_init(struct Small arr[4]) { struct Small a = arr[4]; @@ -144,7 +148,7 @@ __attribute__((noinline)) void test_past_the_end_init(struct Small arr[4]) { // C-LABEL: define {{[^@]*}}@test_assign_atomic_deref_ptr // C: [[SRC:%.*]] = load ptr, ptr %ap.addr // C: icmp ne ptr [[SRC]], null -// C: call void @__ubsan_handle_type_mismatch +// C: call void @__ubsan_handle_type_mismatch_v1_abort( // C: load atomic i32, ptr [[SRC]] seq_cst __attribute__((noinline)) void test_assign_atomic_deref_ptr(struct Small *dest, _Atomic(struct Small) *ap) { *dest = *ap; @@ -162,7 +166,7 @@ extern "C" { // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_direct_plain_arr_idx(struct Small arr[4]) { struct Small a(arr[0]); @@ -172,7 +176,7 @@ __attribute__((noinline)) void test_cxx_init_direct_plain_arr_idx(struct Small a // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_brace_plain_arr_idx(struct Small arr[4]) { struct Small a{arr[0]}; @@ -181,7 +185,7 @@ __attribute__((noinline)) void test_cxx_init_brace_plain_arr_idx(struct Small ar // CXX-LABEL: define {{[^@]*}}@test_cxx_init_direct_plain_deref_ptr // CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_direct_plain_deref_ptr(struct Small *ap) { struct Small a(*ap); @@ -190,7 +194,7 @@ __attribute__((noinline)) void test_cxx_init_direct_plain_deref_ptr(struct Small // CXX-LABEL: define {{[^@]*}}@test_cxx_init_brace_plain_deref_ptr // CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_init_brace_plain_deref_ptr(struct Small *ap) { struct Small a{*ap}; @@ -199,7 +203,7 @@ __attribute__((noinline)) void test_cxx_init_brace_plain_deref_ptr(struct Small // CXX-LABEL: define {{[^@]*}}@test_cxx_new_direct_plain_deref_ptr // CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_new_direct_plain_deref_ptr(struct Small *ap) { struct Small *a = new struct Small(*ap); @@ -211,9 +215,9 @@ __attribute__((noinline)) void test_cxx_new_direct_plain_deref_ptr(struct Small // CXX-LABEL: define {{[^@]*}}@test_cxx_past_the_end_direct // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// CXX-NOT: __ubsan_handle_out_of_bounds +// CXX-NOT: call void @__ubsan_handle_out_of_bounds_abort( // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_past_the_end_direct(struct Small arr[4]) { struct Small a(arr[4]); @@ -222,9 +226,9 @@ __attribute__((noinline)) void test_cxx_past_the_end_direct(struct Small arr[4]) // CXX-LABEL: define {{[^@]*}}@test_cxx_past_the_end_brace // CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr // CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// CXX-NOT: __ubsan_handle_out_of_bounds +// CXX-NOT: call void @__ubsan_handle_out_of_bounds_abort( // CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch +// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( // CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) __attribute__((noinline)) void test_cxx_past_the_end_brace(struct Small arr[4]) { struct Small a{arr[4]}; >From 9972f63b9611a014c22a1322024c90da8ca0b23f Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Tue, 14 Apr 2026 04:59:07 -0400 Subject: [PATCH 4/8] add null/alignment checks for aggregate copy LHS/RHS --- clang/lib/CodeGen/CGExprAgg.cpp | 2 +- .../test/CodeGen/ubsan-aggregate-null-align.c | 347 +++++++----------- 2 files changed, 133 insertions(+), 216 deletions(-) diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 2d6e183e85aee..1668d22be5adc 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1337,7 +1337,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. diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c b/clang/test/CodeGen/ubsan-aggregate-null-align.c index c652a3729fe95..4d1edb7d7af42 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.c +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -1,239 +1,156 @@ -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 %s -o %t.c.ll && FileCheck %s --check-prefixes=C,SHARED < %t.c.ll -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c++17 -x c++ -O0 %s -o %t.cxx.ll && FileCheck %s --check-prefixes=CXX,SHARED < %t.cxx.ll - -// Test for null, alignment, and array-bounds checks on aggregates. -// Verifies that the source operand is checked for null/alignment before memcpy. -// Array bounds checks exist for local arrays. - -struct Small { int x; }; -struct Container { struct Small inner; }; +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 %s -o %t.c.ll && FileCheck %s --check-prefixes=CHECK,C < %t.c.ll +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c++17 -x c++ -O0 %s -o %t.cxx.ll && FileCheck %s --check-prefixes=CHECK,CXX < %t.cxx.ll +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 -DUSE_UNION %s -o %t.union.ll && FileCheck %s --check-prefixes=CHECK,C < %t.union.ll + +#ifdef USE_UNION +union Agg { int x; }; +#define AGG union Agg +#else +struct Agg { int x; }; +#define AGG struct Agg +#endif #ifdef __cplusplus extern "C" { #endif -// Plain type - arr[idx] operand form (known bounds) - -// SHARED-LABEL: define {{[^@]*}}@test_assign_plain_arr_idx -// SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr -// SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_assign_plain_arr_idx(struct Small *dest, struct Small arr[4]) { - *dest = arr[0]; -} - -// SHARED-LABEL: define {{[^@]*}}@test_init_plain_arr_idx -// SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr -// SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_init_plain_arr_idx(struct Small arr[4]) { - struct Small a = arr[0]; -} - -// SHARED-LABEL: define {{[^@]*}}@test_init_list_plain_arr_idx -// SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr -// SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_init_list_plain_arr_idx(struct Small arr[4]) { - struct Small a[] = {arr[0]}; -} - -// Two ubsan calls: one for destination (c), one for source (arr[0]) -// SHARED-LABEL: define {{[^@]*}}@test_nested_member_plain_arr_idx -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64 -__attribute__((noinline)) void test_nested_member_plain_arr_idx(struct Container *c, struct Small arr[4]) { - c->inner = arr[0]; -} - -// Plain type - *ap operand form - -// SHARED-LABEL: define {{[^@]*}}@test_assign_plain_deref_ptr -// SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_assign_plain_deref_ptr(struct Small *dest, struct Small *ap) { - *dest = *ap; -} - -// SHARED-LABEL: define {{[^@]*}}@test_init_plain_deref_ptr -// SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_init_plain_deref_ptr(struct Small *ap) { - struct Small a = *ap; -} - -// SHARED-LABEL: define {{[^@]*}}@test_init_list_plain_deref_ptr -// SHARED: [[SRC:%.*]] = load ptr, ptr %ap.addr -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_init_list_plain_deref_ptr(struct Small *ap) { - struct Small a[] = {*ap}; -} - -// Two ubsan calls: one for destination (c), one for source (*ap) -// SHARED-LABEL: define {{[^@]*}}@test_nested_member_plain_deref_ptr -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64 -__attribute__((noinline)) void test_nested_member_plain_deref_ptr(struct Container *c, struct Small *ap) { - c->inner = *ap; -} - -// Misaligned aggregate access - -// SHARED-LABEL: define {{[^@]*}}@test_misaligned_access -// SHARED: icmp ne ptr -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy -__attribute__((noinline)) void test_misaligned_access(struct Small *dest, char *buf) { - struct Small *p = (struct Small *)(buf + 1); // Misaligned - *dest = *p; -} - -// Array bounds: out-of-bounds on local array - -// SHARED-LABEL: define {{[^@]*}}@test_local_array_oob -// SHARED: call void @__ubsan_handle_out_of_bounds_abort( -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64 -__attribute__((noinline)) void test_local_array_oob(struct Small *dest) { - struct Small arr[4]; - *dest = arr[5]; -} - -// Array bounds: past-the-end via parameter - -// SHARED-LABEL: define {{[^@]*}}@test_past_the_end_arr_idx -// SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr -// SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// SHARED-NOT: call void @__ubsan_handle_out_of_bounds_abort( -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_past_the_end_arr_idx(struct Small *dest, struct Small arr[4]) { - *dest = arr[4]; -} - -// SHARED-LABEL: define {{[^@]*}}@test_past_the_end_init -// SHARED: [[ARR:%.*]] = load ptr, ptr %arr.addr -// SHARED: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// SHARED-NOT: call void @__ubsan_handle_out_of_bounds_abort( -// SHARED: icmp ne ptr [[SRC]], null -// SHARED: call void @__ubsan_handle_type_mismatch_v1_abort( -// SHARED: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_past_the_end_init(struct Small arr[4]) { - struct Small a = arr[4]; +// LHS checks - C only + +// C-LABEL: define {{.*}}@test_lhs_ptr( +// C: [[DEST:%.*]] = load ptr, ptr %dest.addr +// C-NEXT: [[CMP:%.*]] = icmp ne ptr [[DEST]], null, !nosanitize +// C-NEXT: [[INT:%.*]] = ptrtoint ptr [[DEST]] to i64, !nosanitize +// C-NEXT: [[AND:%.*]] = and i64 [[INT]], 3, !nosanitize +// C-NEXT: [[ALIGN:%.*]] = icmp eq i64 [[AND]], 0, !nosanitize +// C-NEXT: [[OK:%.*]] = and i1 [[CMP]], [[ALIGN]], !nosanitize +// C-NEXT: br i1 [[OK]], label %cont, label %handler.type_mismatch +// C: handler.type_mismatch: +// C-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// C: call void @llvm.memcpy +void test_lhs_ptr(AGG *dest) { + AGG local = {0}; + *dest = local; +} + +// C-LABEL: define {{.*}}@test_lhs_array( +// C: [[ARR:%.*]] = load ptr, ptr %arr.addr +// C-NEXT: [[DEST:%.*]] = getelementptr inbounds %{{(struct|union)}}.Agg, ptr [[ARR]], i64 0 +// C-NEXT: [[CMP:%.*]] = icmp ne ptr [[DEST]], null, !nosanitize +// C: handler.type_mismatch: +// C-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// C: call void @llvm.memcpy +void test_lhs_array(AGG arr[4]) { + AGG local = {0}; + arr[0] = local; +} + +// RHS checks - both C and C++ + +// CHECK-LABEL: define {{.*}}@test_rhs_ptr( +// CHECK: [[SRC:%.*]] = load ptr, ptr %src.addr +// CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[SRC]], null, !nosanitize +// CHECK-NEXT: [[INT:%.*]] = ptrtoint ptr [[SRC]] to i64, !nosanitize +// CHECK-NEXT: [[AND:%.*]] = and i64 [[INT]], 3, !nosanitize +// CHECK-NEXT: [[ALIGN:%.*]] = icmp eq i64 [[AND]], 0, !nosanitize +// CHECK-NEXT: [[OK:%.*]] = and i1 [[CMP]], [[ALIGN]], !nosanitize +// CHECK-NEXT: br i1 [[OK]], label %cont, label %handler.type_mismatch +// CHECK: handler.type_mismatch: +// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy +void test_rhs_ptr(AGG *src) { + AGG local; + local = *src; + (void)local; +} + +// CHECK-LABEL: define {{.*}}@test_rhs_array( +// CHECK: [[ARR:%.*]] = load ptr, ptr %arr.addr +// CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds %{{(struct|union)}}.Agg, ptr [[ARR]], i64 0 +// CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[SRC]], null, !nosanitize +// CHECK: handler.type_mismatch: +// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CHECK: call void @llvm.memcpy +void test_rhs_array(AGG arr[4]) { + AGG local; + local = arr[0]; + (void)local; +} + +// RHS cases - handler call only + +// CHECK-LABEL: define {{.*}}@test_init_from_ptr( +// CHECK: handler.type_mismatch: +// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CHECK: call void @llvm.memcpy +void test_init_from_ptr(AGG *src) { + AGG local = *src; + (void)local; +} + +// CHECK-LABEL: define {{.*}}@test_init_from_array( +// CHECK: handler.type_mismatch: +// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CHECK: call void @llvm.memcpy +void test_init_from_array(AGG arr[4]) { + AGG local = arr[0]; + (void)local; +} + +// Array bounds - out-of-bounds access + +// CHECK-LABEL: define {{.*}}@test_oob_rhs( +// CHECK: br i1 {{(true|false)}}, label %cont, label %handler.out_of_bounds +// CHECK: handler.out_of_bounds: +// CHECK-NEXT: call void @__ubsan_handle_out_of_bounds_abort +// CHECK: handler.type_mismatch: +// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CHECK: call void @llvm.memcpy +void test_oob_rhs(void) { + AGG arr[4]; + AGG local; + local = arr[4]; + (void)local; } #ifdef __cplusplus -} // extern "C" -#endif - -// Atomic type (C only) - -#ifndef __cplusplus - -// C-LABEL: define {{[^@]*}}@test_assign_atomic_deref_ptr -// C: [[SRC:%.*]] = load ptr, ptr %ap.addr -// C: icmp ne ptr [[SRC]], null -// C: call void @__ubsan_handle_type_mismatch_v1_abort( -// C: load atomic i32, ptr [[SRC]] seq_cst -__attribute__((noinline)) void test_assign_atomic_deref_ptr(struct Small *dest, _Atomic(struct Small) *ap) { - *dest = *ap; } +#endif -#endif // !__cplusplus -// C++ only +// C++ - handler call only #ifdef __cplusplus extern "C" { -// CXX-LABEL: define {{[^@]*}}@test_cxx_init_direct_plain_arr_idx -// CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr -// CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_init_direct_plain_arr_idx(struct Small arr[4]) { - struct Small a(arr[0]); -} - -// CXX-LABEL: define {{[^@]*}}@test_cxx_init_brace_plain_arr_idx -// CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr -// CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 0 -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_init_brace_plain_arr_idx(struct Small arr[4]) { - struct Small a{arr[0]}; -} - -// CXX-LABEL: define {{[^@]*}}@test_cxx_init_direct_plain_deref_ptr -// CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_init_direct_plain_deref_ptr(struct Small *ap) { - struct Small a(*ap); -} - -// CXX-LABEL: define {{[^@]*}}@test_cxx_init_brace_plain_deref_ptr -// CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_init_brace_plain_deref_ptr(struct Small *ap) { - struct Small a{*ap}; +// CXX-LABEL: define {{.*}}@test_cxx_direct_init( +// CXX: handler.type_mismatch: +// CXX-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CXX: call void @llvm.memcpy +void test_cxx_direct_init(AGG *src) { + AGG local(*src); + (void)local; } -// CXX-LABEL: define {{[^@]*}}@test_cxx_new_direct_plain_deref_ptr -// CXX: [[SRC:%.*]] = load ptr, ptr %ap.addr -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_new_direct_plain_deref_ptr(struct Small *ap) { - struct Small *a = new struct Small(*ap); - delete a; +// CXX-LABEL: define {{.*}}@test_cxx_brace_init( +// CXX: handler.type_mismatch: +// CXX-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CXX: call void @llvm.memcpy +void test_cxx_brace_init(AGG *src) { + AGG local{*src}; + (void)local; } -// C++ past-the-end tests - -// CXX-LABEL: define {{[^@]*}}@test_cxx_past_the_end_direct -// CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr -// CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// CXX-NOT: call void @__ubsan_handle_out_of_bounds_abort( -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_past_the_end_direct(struct Small arr[4]) { - struct Small a(arr[4]); +// CXX-LABEL: define {{.*}}@test_cxx_new( +// CXX: handler.type_mismatch: +// CXX-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// CXX: call void @llvm.memcpy +void test_cxx_new(AGG *src) { + AGG *p = new AGG(*src); + delete p; } -// CXX-LABEL: define {{[^@]*}}@test_cxx_past_the_end_brace -// CXX: [[ARR:%.*]] = load ptr, ptr %arr.addr -// CXX: [[SRC:%.*]] = getelementptr inbounds %struct.Small, ptr [[ARR]], i64 4 -// CXX-NOT: call void @__ubsan_handle_out_of_bounds_abort( -// CXX: icmp ne ptr [[SRC]], null -// CXX: call void @__ubsan_handle_type_mismatch_v1_abort( -// CXX: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 [[SRC]], i64 4, i1 false) -__attribute__((noinline)) void test_cxx_past_the_end_brace(struct Small arr[4]) { - struct Small a{arr[4]}; } -} // extern "C" - -#endif // __cplusplus +#endif >From fa88d481a2cd63811b1ca1a389fd9b519b4d8627 Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Tue, 21 Apr 2026 02:49:21 -0400 Subject: [PATCH 5/8] update ubsan-new-checks.cpp --- clang/test/CodeGenCXX/ubsan-new-checks.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/test/CodeGenCXX/ubsan-new-checks.cpp b/clang/test/CodeGenCXX/ubsan-new-checks.cpp index adc54ccbb00b7..9adc0840801b9 100644 --- a/clang/test/CodeGenCXX/ubsan-new-checks.cpp +++ b/clang/test/CodeGenCXX/ubsan-new-checks.cpp @@ -136,12 +136,12 @@ S4 *func_14() { return new S4; } -S5 *func_15(const S5 *ptr) { - // CHECK-LABEL: define {{.*}} @_Z7func_15PK2S5 - // CHECK: and i64 %{{.*}}, 31, !nosanitize - // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize +S5 *func_15() { + // CHECK-LABEL: define {{.*}} @_Z7func_15v // CHECK: and i64 %{{.*}}, 31, !nosanitize // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK-NOT: and i64 // CHECK: ret ptr - return new S5(*ptr); + S5 local = {}; + return new S5(local); } >From 58d4075b756f35214b4d6f64d89a9ea3e23e0e1f Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Wed, 22 Apr 2026 03:44:20 -0400 Subject: [PATCH 6/8] resolve review comments --- .../test/CodeGen/ubsan-aggregate-null-align.c | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c b/clang/test/CodeGen/ubsan-aggregate-null-align.c index 4d1edb7d7af42..d5b6de3f456c0 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.c +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 %s -o %t.c.ll && FileCheck %s --check-prefixes=CHECK,C < %t.c.ll -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c++17 -x c++ -O0 %s -o %t.cxx.ll && FileCheck %s --check-prefixes=CHECK,CXX < %t.cxx.ll -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 -DUSE_UNION %s -o %t.union.ll && FileCheck %s --check-prefixes=CHECK,C < %t.union.ll +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 %s -o - | FileCheck %s --check-prefixes=CHECK,C +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c++17 -x c++ -O0 %s -o - | FileCheck %s --check-prefixes=CHECK,CXX +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c11 -O0 -DUSE_UNION %s -o - | FileCheck %s --check-prefixes=CHECK,C +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=null,alignment,array-bounds -std=c++17 -x c++ -O0 -DUSE_UNION %s -o - | FileCheck %s --check-prefixes=CHECK,CXX #ifdef USE_UNION union Agg { int x; }; @@ -16,7 +17,7 @@ extern "C" { // LHS checks - C only -// C-LABEL: define {{.*}}@test_lhs_ptr( +// C-LABEL: define {{.*}}@test_lhs_ptrcheck_deref( // C: [[DEST:%.*]] = load ptr, ptr %dest.addr // C-NEXT: [[CMP:%.*]] = icmp ne ptr [[DEST]], null, !nosanitize // C-NEXT: [[INT:%.*]] = ptrtoint ptr [[DEST]] to i64, !nosanitize @@ -27,26 +28,26 @@ extern "C" { // C: handler.type_mismatch: // C-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort // C: call void @llvm.memcpy -void test_lhs_ptr(AGG *dest) { +void test_lhs_ptrcheck_deref(AGG *dest) { AGG local = {0}; *dest = local; } -// C-LABEL: define {{.*}}@test_lhs_array( +// C-LABEL: define {{.*}}@test_lhs_ptrcheck_subscript( // C: [[ARR:%.*]] = load ptr, ptr %arr.addr // C-NEXT: [[DEST:%.*]] = getelementptr inbounds %{{(struct|union)}}.Agg, ptr [[ARR]], i64 0 // C-NEXT: [[CMP:%.*]] = icmp ne ptr [[DEST]], null, !nosanitize // C: handler.type_mismatch: // C-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort // C: call void @llvm.memcpy -void test_lhs_array(AGG arr[4]) { +void test_lhs_ptrcheck_subscript(AGG arr[4]) { AGG local = {0}; arr[0] = local; } // RHS checks - both C and C++ -// CHECK-LABEL: define {{.*}}@test_rhs_ptr( +// CHECK-LABEL: define {{.*}}@test_rhs_ptrcheck_deref( // CHECK: [[SRC:%.*]] = load ptr, ptr %src.addr // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[SRC]], null, !nosanitize // CHECK-NEXT: [[INT:%.*]] = ptrtoint ptr [[SRC]] to i64, !nosanitize @@ -58,20 +59,20 @@ void test_lhs_array(AGG arr[4]) { // CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort // CHECK: cont: // CHECK-NEXT: call void @llvm.memcpy -void test_rhs_ptr(AGG *src) { +void test_rhs_ptrcheck_deref(AGG *src) { AGG local; local = *src; (void)local; } -// CHECK-LABEL: define {{.*}}@test_rhs_array( +// CHECK-LABEL: define {{.*}}@test_rhs_ptrcheck_subscript( // CHECK: [[ARR:%.*]] = load ptr, ptr %arr.addr // CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds %{{(struct|union)}}.Agg, ptr [[ARR]], i64 0 // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[SRC]], null, !nosanitize // CHECK: handler.type_mismatch: // CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort // CHECK: call void @llvm.memcpy -void test_rhs_array(AGG arr[4]) { +void test_rhs_ptrcheck_subscript(AGG arr[4]) { AGG local; local = arr[0]; (void)local; @@ -97,7 +98,7 @@ void test_init_from_array(AGG arr[4]) { (void)local; } -// Array bounds - out-of-bounds access +// Array bounds - out-of-bounds access (RHS) // CHECK-LABEL: define {{.*}}@test_oob_rhs( // CHECK: br i1 {{(true|false)}}, label %cont, label %handler.out_of_bounds @@ -113,12 +114,27 @@ void test_oob_rhs(void) { (void)local; } +// Array bounds - out-of-bounds access (LHS) + +// C-LABEL: define {{.*}}@test_oob_lhs( +// C: br i1 {{(true|false)}}, label %cont, label %handler.out_of_bounds +// C: handler.out_of_bounds: +// C-NEXT: call void @__ubsan_handle_out_of_bounds_abort +// C: handler.type_mismatch: +// C-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort +// C: call void @llvm.memcpy +void test_oob_lhs(void) { + AGG arr[4]; + AGG local = {0}; + arr[4] = local; + (void)arr; +} + #ifdef __cplusplus } #endif - -// C++ - handler call only +// C++ RHS cases - handler call only #ifdef __cplusplus >From 24ab5af544afeb925012102ffe0f34da72338681 Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Wed, 6 May 2026 02:50:25 -0400 Subject: [PATCH 7/8] add C++ checks for test_oob_lhs --- clang/test/CodeGen/ubsan-aggregate-null-align.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c b/clang/test/CodeGen/ubsan-aggregate-null-align.c index d5b6de3f456c0..579adb3f1c9ea 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.c +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -116,13 +116,13 @@ void test_oob_rhs(void) { // Array bounds - out-of-bounds access (LHS) -// C-LABEL: define {{.*}}@test_oob_lhs( -// C: br i1 {{(true|false)}}, label %cont, label %handler.out_of_bounds -// C: handler.out_of_bounds: -// C-NEXT: call void @__ubsan_handle_out_of_bounds_abort +// CHECK-LABEL: define {{.*}}@test_oob_lhs( +// CHECK: br i1 {{(true|false)}}, label %cont, label %handler.out_of_bounds +// CHECK: handler.out_of_bounds: +// CHECK-NEXT: call void @__ubsan_handle_out_of_bounds_abort // C: handler.type_mismatch: // C-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort -// C: call void @llvm.memcpy +// CHECK: call void @llvm.memcpy void test_oob_lhs(void) { AGG arr[4]; AGG local = {0}; >From fb878e76571bba910ce938d70bbcbe7e14cf686e Mon Sep 17 00:00:00 2001 From: vasu-ibm <[email protected]> Date: Fri, 8 May 2026 11:07:41 -0400 Subject: [PATCH 8/8] add comment for follow-up PR --- clang/test/CodeGen/ubsan-aggregate-null-align.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c b/clang/test/CodeGen/ubsan-aggregate-null-align.c index 579adb3f1c9ea..ebec72b05c657 100644 --- a/clang/test/CodeGen/ubsan-aggregate-null-align.c +++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c @@ -16,6 +16,9 @@ extern "C" { #endif // LHS checks - C only +// Note: In C++, aggregate assignment goes through operator= +// which is a different code path (CGExprCXX.cpp). +// LHS checks for C++ will be addressed in a follow-up PR. // C-LABEL: define {{.*}}@test_lhs_ptrcheck_deref( // C: [[DEST:%.*]] = load ptr, ptr %dest.addr _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
