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

Reply via email to