[clang] [Clang][CodeGen] Enable pointer overflow check for GCC workaround (PR #137849)
Yingwei Zheng via cfe-commits
cfe-commits at lists.llvm.org
Thu May 8 01:45:35 PDT 2025
https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/137849
>From 5b3a9ed3ed2f258a178e0c17891e2d9ae4f21446 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 6 May 2025 17:47:07 +0800
Subject: [PATCH 1/3] [Clang][CodeGen] Enable pointer overflow check for GCC
workaround
---
clang/lib/CodeGen/CGExprScalar.cpp | 8 ++---
.../catch-nullptr-and-nonzero-offset.c | 36 +++++++++++++++++--
2 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 8dbbcdaef25d8..f7de0bfa2d76c 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4169,10 +4169,10 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
// The index is not pointer-sized.
// The pointer type is not byte-sized.
//
- if (BinaryOperator::isNullPointerArithmeticExtension(CGF.getContext(),
- op.Opcode,
- expr->getLHS(),
- expr->getRHS()))
+ // Note that we do not suppress the pointer overflow check in this case.
+ if (!CGF.SanOpts.has(SanitizerKind::PointerOverflow) &&
+ BinaryOperator::isNullPointerArithmeticExtension(
+ CGF.getContext(), op.Opcode, expr->getLHS(), expr->getRHS()))
return CGF.Builder.CreateIntToPtr(index, pointer->getType());
if (width != DL.getIndexTypeSizeInBits(PtrTy)) {
diff --git a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
index 63b6db2c2adeb..0cf147d27a3dd 100644
--- a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
+++ b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
-// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
@@ -32,6 +32,7 @@
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1500:.*]] = {{.*}}, i32 1500, i32 15 } }
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1600:.*]] = {{.*}}, i32 1600, i32 15 } }
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1700:.*]] = {{.*}}, i32 1700, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1800:.*]] = {{.*}}, i32 1800, i32 20 } }
#ifdef __cplusplus
extern "C" {
@@ -431,6 +432,37 @@ char *void_ptr(void *base, unsigned long offset) {
return base + offset;
}
+char *constant_null_add(unsigned long offset) {
+ // CHECK: define{{.*}} ptr @constant_null_add(i64 noundef %[[OFFSET:.*]])
+ // CHECK-NEXT: [[ENTRY:.*]]:
+ // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+ // CHECK-NEXT: store i64 %[[OFFSET]], ptr %[[OFFSET_ADDR]], align 8
+ // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, ptr %[[OFFSET_ADDR]], align 8
+ // CHECK-NOSANITIZE-NEXT: %[[ADD_PTR:.*]] = inttoptr i64 %[[OFFSET_RELOADED]] to ptr
+ // CHECK-SANITIZE-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds nuw i8, ptr null, i64 %[[OFFSET_RELOADED]]
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[OR_OV:.+]] = or i1 %[[COMPUTED_OFFSET_OVERFLOWED]], false, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 0, %[[COMPUTED_OFFSET]], !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 false, %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[OR_OV]], true, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1800]], i64 0, i64 %[[COMPUTED_GEP]])
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1800]], i64 0, i64 %[[COMPUTED_GEP]])
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret ptr %[[ADD_PTR]]
+#line 1800
+ return (char *)0 + offset;
+}
+
#ifdef __cplusplus
}
#endif
>From ac8207cfe778c7dea7fcbf7bbc8cf22b56e8e547 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 8 May 2025 16:15:44 +0800
Subject: [PATCH 2/3] [Clang][CodeGen] Emit null check
---
clang/lib/CodeGen/CGExprScalar.cpp | 24 +++++++++++++++---
.../catch-nullptr-and-nonzero-offset.c | 25 ++++++-------------
2 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index f7de0bfa2d76c..0633968e6f6cb 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4170,10 +4170,26 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
// The pointer type is not byte-sized.
//
// Note that we do not suppress the pointer overflow check in this case.
- if (!CGF.SanOpts.has(SanitizerKind::PointerOverflow) &&
- BinaryOperator::isNullPointerArithmeticExtension(
- CGF.getContext(), op.Opcode, expr->getLHS(), expr->getRHS()))
- return CGF.Builder.CreateIntToPtr(index, pointer->getType());
+ if (BinaryOperator::isNullPointerArithmeticExtension(
+ CGF.getContext(), op.Opcode, expr->getLHS(), expr->getRHS())) {
+ Value *Ptr = CGF.Builder.CreateIntToPtr(index, pointer->getType());
+ if (CGF.getLangOpts().PointerOverflowDefined ||
+ CGF.CGM.getCodeGenOpts().NullPointerIsValid ||
+ !CGF.SanOpts.has(SanitizerKind::PointerOverflow))
+ return Ptr;
+ // The inbounds GEP of null is valid iff the index is zero.
+ CodeGenFunction::SanitizerScope SanScope(&CGF);
+ Value *IsZeroIndex = CGF.Builder.CreateIsNull(index);
+ llvm::Constant *StaticArgs[] = {
+ CGF.EmitCheckSourceLocation(op.E->getExprLoc())};
+ llvm::Type *IntPtrTy = DL.getIntPtrType(PtrTy);
+ Value *IntPtr = llvm::Constant::getNullValue(IntPtrTy);
+ Value *ComputedGEP = CGF.Builder.CreateZExtOrTrunc(index, IntPtrTy);
+ Value *DynamicArgs[] = {IntPtr, ComputedGEP};
+ CGF.EmitCheck({{IsZeroIndex, SanitizerKind::SO_PointerOverflow}},
+ SanitizerHandler::PointerOverflow, StaticArgs, DynamicArgs);
+ return Ptr;
+ }
if (width != DL.getIndexTypeSizeInBits(PtrTy)) {
// Zero-extend or sign-extend the pointer value according to
diff --git a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
index 0cf147d27a3dd..26d17e7b23aaf 100644
--- a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
+++ b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
+// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
-// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
+// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
@@ -438,23 +438,12 @@ char *constant_null_add(unsigned long offset) {
// CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8
// CHECK-NEXT: store i64 %[[OFFSET]], ptr %[[OFFSET_ADDR]], align 8
// CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, ptr %[[OFFSET_ADDR]], align 8
- // CHECK-NOSANITIZE-NEXT: %[[ADD_PTR:.*]] = inttoptr i64 %[[OFFSET_RELOADED]] to ptr
- // CHECK-SANITIZE-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds nuw i8, ptr null, i64 %[[OFFSET_RELOADED]]
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[OR_OV:.+]] = or i1 %[[COMPUTED_OFFSET_OVERFLOWED]], false, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 0, %[[COMPUTED_OFFSET]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 false, %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[OR_OV]], true, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+ // CHECK-NEXT: %[[ADD_PTR:.*]] = inttoptr i64 %[[OFFSET_RELOADED]] to ptr
+ // CHECK-SANITIZE-NEXT: %[[IS_NULL:.*]] = icmp eq i64 %[[OFFSET_RELOADED]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[IS_NULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
// CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1800]], i64 0, i64 %[[COMPUTED_GEP]])
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1800]], i64 0, i64 %[[COMPUTED_GEP]])
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1800]], i64 0, i64 %[[OFFSET_RELOADED]])
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1800]], i64 0, i64 %[[OFFSET_RELOADED]])
// CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
// CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
// CHECK-SANITIZE: [[CONT]]:
>From d4f26e60e1984463acf23ee7d0c3961b458f0801 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 8 May 2025 16:44:15 +0800
Subject: [PATCH 3/3] [Clang][CodeGen] Use `NullPointerIsDefined`
---
clang/lib/CodeGen/CGExprScalar.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 0633968e6f6cb..05b26b8e3ea78 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4174,8 +4174,9 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
CGF.getContext(), op.Opcode, expr->getLHS(), expr->getRHS())) {
Value *Ptr = CGF.Builder.CreateIntToPtr(index, pointer->getType());
if (CGF.getLangOpts().PointerOverflowDefined ||
- CGF.CGM.getCodeGenOpts().NullPointerIsValid ||
- !CGF.SanOpts.has(SanitizerKind::PointerOverflow))
+ !CGF.SanOpts.has(SanitizerKind::PointerOverflow) ||
+ NullPointerIsDefined(CGF.Builder.GetInsertBlock()->getParent(),
+ PtrTy->getPointerAddressSpace()))
return Ptr;
// The inbounds GEP of null is valid iff the index is zero.
CodeGenFunction::SanitizerScope SanScope(&CGF);
More information about the cfe-commits
mailing list