https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/137851
>From 5b3a9ed3ed2f258a178e0c17891e2d9ae4f21446 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 6 May 2025 17:47:07 +0800 Subject: [PATCH 1/4] [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 ba68a7857edea4c7bed653d68edac47ca1c81922 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 6 May 2025 17:57:36 +0800 Subject: [PATCH 2/4] [Clang][CodeGen] Add pre-commit tests. NFC. --- clang/test/CodeGen/glibc_ptr_align.c | 62 ++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 clang/test/CodeGen/glibc_ptr_align.c diff --git a/clang/test/CodeGen/glibc_ptr_align.c b/clang/test/CodeGen/glibc_ptr_align.c new file mode 100644 index 0000000000000..693812b7c6aaa --- /dev/null +++ b/clang/test/CodeGen/glibc_ptr_align.c @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -O3 -o - -emit-llvm %s | FileCheck %s + +// Make sure that we do not set inbounds flag if the base pointer may be a constant null. + +// CHECK-LABEL: define dso_local noalias ptr @glibc_ptr_align( +// CHECK-SAME: ptr noundef readnone captures(none) [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]] +// CHECK-NEXT: [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1 +// CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[NOT]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr null, i64 [[AND]] +// CHECK-NEXT: ret ptr [[ADD_PTR]] +// +char *glibc_ptr_align(char *base, char *pointer, long align_mask) { + return (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) + + (((pointer) - + (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) + + (align_mask)) & + ~(align_mask)); +} + +// CHECK-LABEL: define dso_local noalias ptr @glibc_ptr_align_commuted( +// CHECK-SAME: ptr noundef readnone captures(none) [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]] +// CHECK-NEXT: [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1 +// CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[NOT]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr null, i64 [[AND]] +// CHECK-NEXT: ret ptr [[ADD_PTR]] +// +char *glibc_ptr_align_commuted(char *base, char *pointer, long align_mask) { + return (sizeof(long int) >= sizeof(void *) ? (char *)0 : (base)) + + (((pointer) - + (sizeof(long int) >= sizeof(void *) ? (char *)0 : (base)) + + (align_mask)) & + ~(align_mask)); +} + +// CHECK-LABEL: define dso_local ptr @glibc_ptr_align_non_constexpr( +// CHECK-SAME: ptr noundef [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]], i32 noundef [[COND:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[COND]], 0 +// CHECK-NEXT: [[COND1:%.*]] = select i1 [[TOBOOL_NOT]], ptr null, ptr [[BASE]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[COND1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = add i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]] +// CHECK-NEXT: [[ADD:%.*]] = sub i64 [[SUB_PTR_SUB]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1 +// CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[NOT]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[COND1]], i64 [[AND]] +// CHECK-NEXT: ret ptr [[ADD_PTR]] +// +char *glibc_ptr_align_non_constexpr(char *base, char *pointer, long align_mask, int cond) { + return (cond ? (base) : (char *)0) + + (((pointer) - + (cond ? (base) : (char *)0) + + (align_mask)) & + ~(align_mask)); +} >From 419863eb5169044d3c9f8350fdb3b172365f1b73 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 6 May 2025 19:54:37 +0800 Subject: [PATCH 3/4] [Clang][CodeGen] Add workaround for old glibc `__PTR_ALIGN` macro --- clang/lib/AST/Expr.cpp | 9 +++++++++ clang/test/CodeGen/glibc_ptr_align.c | 12 ++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 59c0e47c7c195..927b340baede1 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2247,6 +2247,15 @@ bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx, return false; } + // Workaround for old glibc's __PTR_ALIGN macro + if (auto *Select = dyn_cast<ConditionalOperator>(PExp->IgnoreParenCasts())) { + // If the condition can be constant evaluated, we check the selected arm. + bool EvalResult; + if (!Select->getCond()->EvaluateAsBooleanCondition(EvalResult, Ctx)) + return false; + PExp = EvalResult ? Select->getTrueExpr() : Select->getFalseExpr(); + } + // Check that the pointer is a nullptr. if (!PExp->IgnoreParenCasts() ->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) diff --git a/clang/test/CodeGen/glibc_ptr_align.c b/clang/test/CodeGen/glibc_ptr_align.c index 693812b7c6aaa..0656d61eb0caa 100644 --- a/clang/test/CodeGen/glibc_ptr_align.c +++ b/clang/test/CodeGen/glibc_ptr_align.c @@ -3,15 +3,15 @@ // Make sure that we do not set inbounds flag if the base pointer may be a constant null. -// CHECK-LABEL: define dso_local noalias ptr @glibc_ptr_align( +// CHECK-LABEL: define dso_local ptr @glibc_ptr_align( // CHECK-SAME: ptr noundef readnone captures(none) [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64 // CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]] // CHECK-NEXT: [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1 // CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[NOT]] -// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr null, i64 [[AND]] -// CHECK-NEXT: ret ptr [[ADD_PTR]] +// CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[AND]] to ptr +// CHECK-NEXT: ret ptr [[TMP0]] // char *glibc_ptr_align(char *base, char *pointer, long align_mask) { return (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) + @@ -21,15 +21,15 @@ char *glibc_ptr_align(char *base, char *pointer, long align_mask) { ~(align_mask)); } -// CHECK-LABEL: define dso_local noalias ptr @glibc_ptr_align_commuted( +// CHECK-LABEL: define dso_local ptr @glibc_ptr_align_commuted( // CHECK-SAME: ptr noundef readnone captures(none) [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64 // CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]] // CHECK-NEXT: [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1 // CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[NOT]] -// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr null, i64 [[AND]] -// CHECK-NEXT: ret ptr [[ADD_PTR]] +// CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[AND]] to ptr +// CHECK-NEXT: ret ptr [[TMP0]] // char *glibc_ptr_align_commuted(char *base, char *pointer, long align_mask) { return (sizeof(long int) >= sizeof(void *) ? (char *)0 : (base)) + >From aa4acdec652c212dadfd38bf612fa5f1975b8128 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Wed, 7 May 2025 14:25:15 +0800 Subject: [PATCH 4/4] [Clang][CodeGen] Address review comments. --- clang/lib/AST/Expr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 927b340baede1..e2bb74f5bf43c 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2248,7 +2248,8 @@ bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx, } // Workaround for old glibc's __PTR_ALIGN macro - if (auto *Select = dyn_cast<ConditionalOperator>(PExp->IgnoreParenCasts())) { + if (auto *Select = + dyn_cast<ConditionalOperator>(PExp->IgnoreParenNoopCasts(Ctx))) { // If the condition can be constant evaluated, we check the selected arm. bool EvalResult; if (!Select->getCond()->EvaluateAsBooleanCondition(EvalResult, Ctx)) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits