diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index eee397f1f3d19..cf215da08fbf1 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -2249,6 +2249,22 @@ 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(); 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; +}