blob: 41f44fb088bf157ea59f0725ed8af37885c229c4 [file] [edit]
// 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: %[[P:.*]] = load ptr, ptr %p
// 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