Author: VASU SHARMA
Date: 2026-04-01T14:04:28+05:30
New Revision: 2313989499b5ee9befd9865310f6fa02f561cf63

URL: 
https://github.com/llvm/llvm-project/commit/2313989499b5ee9befd9865310f6fa02f561cf63
DIFF: 
https://github.com/llvm/llvm-project/commit/2313989499b5ee9befd9865310f6fa02f561cf63.diff

LOG: [UBSAN] [NFC] pre-commit tests for null, alignment, bounds checks (#176210)

PR to add precommit tests to document current UBSAN behavior for
aggregate copy operations.

The test covers:
- Sanitizers: null, alignment, bounds
- Type variants: plain, _Atomic (C), volatile (C)
- Operand forms: arr[idx], *ptr
- Operations: assignment, initialization, initializer list, variadic
args, nested member access
- C++ specific: direct/brace/copy-list init, new expressions, casts,
operator=, virtual base init
- Bounds checking: in-bounds access, past-the-end access (index ==
size), beyond bounds access, dynamic index

---------

Co-authored-by: vasu-ibm <[email protected]>
Co-authored-by: Tony Varghese <[email protected]>

Added: 
    clang/test/CodeGen/ubsan-aggregate-null-align.c

Modified: 
    

Removed: 
    


################################################################################
diff  --git a/clang/test/CodeGen/ubsan-aggregate-null-align.c 
b/clang/test/CodeGen/ubsan-aggregate-null-align.c
new file mode 100644
index 0000000000000..1e2b60d7bde14
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-aggregate-null-align.c
@@ -0,0 +1,235 @@
+// 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.
+
+struct Small { int x; };
+struct Container { struct Small inner; };
+
+#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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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: 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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: 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-NOT: 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-NOT: 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// SHARED-NOT: icmp ne ptr [[SRC]], null
+// 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];
+}
+
+#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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// C-NOT: icmp ne ptr [[SRC]], null
+// 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 // !__cplusplus
+
+// C++ 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// CXX-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// CXX-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// CXX-NOT: icmp ne ptr [[SRC]], null
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// CXX-NOT: icmp ne ptr [[SRC]], null
+// 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_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: 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;
+}
+
+// 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-NOT: call void @__ubsan_handle_type_mismatch_v1_abort
+// CXX-NOT: icmp ne ptr [[SRC]], null
+// 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_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: 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


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to