https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/229031
Fixes #170139 Fixes #120082 `__atomic_always_lock_free` and `__atomic_is_lock_free` check the size with `CharUnits::isPowerOfTwo()`, which is also true for zero. So a size of 0 was treated as a lock-free size, and when the pointer argument was a constant we went on to test its alignment against that size, which builds `llvm::Align(0)` and asserts. Without assertions nothing crashed, but the answer depended on how the pointer was written: `__atomic_always_lock_free(0, p)` was true for a null pointer or `&obj` and false for `(void *)4`. Both constant evaluators now require the size to be positive, so 0 is handled like any other size that isn't lock-free, such as 3: `__atomic_always_lock_free` is false, and `__atomic_is_lock_free` and `__c11_atomic_is_lock_free` are not folded and end up as the runtime call. GCC does the same. I left `CharUnits::isPowerOfTwo()` alone because ObjC property codegen relies on it accepting a zero-sized ivar. The positive-size check also rejects `2^63`, which wraps to a negative `CharUnits` and used to be reported as lock-free. LLM tools (Gemini 3.1 Pro, not Agent) were used for this contribution. I've reviewed, built, and tested the change myself before pushing to GitHub. >From 93feb51113746ea61bf58266f44da658db975d2a Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 5 Oct 2026 15:22:30 +0530 Subject: [PATCH] [Clang] Fix crash on `__atomic_always_lock_free` with a size of zero `CharUnits::isPowerOfTwo()` is also true for zero, so the constant evaluators accepted a size of 0 as lock-free and built `llvm::Align(0)` when checking the alignment of a constant pointer argument. Require a positive size in the classic evaluator and in the bytecode interpreter. A size of 0 is now handled like any other size that is not lock-free: `__atomic_always_lock_free` is false, and `__atomic_is_lock_free` and `__c11_atomic_is_lock_free` are not folded. Fixes #170139 Fixes #120082 --- clang/docs/ReleaseNotes.md | 2 ++ clang/lib/AST/ByteCode/InterpBuiltin.cpp | 4 ++-- clang/lib/AST/ExprConstant.cpp | 2 +- clang/test/AST/ByteCode/atomic.c | 14 ++++++++++++++ clang/test/CodeGen/atomic-ops.c | 16 ++++++++++++++++ clang/test/Sema/atomic-ops.c | 12 ++++++++++++ 6 files changed, 47 insertions(+), 3 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index bf194e87f76a6..6ff60826e5648 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -605,6 +605,8 @@ features cannot lower the translation-unit ABI level; inside a member function call synthesized by ``__builtin_invoke``. (#GH185241) - Fixed a crash in ``__builtin_dump_struct`` when ``-Werror`` promotes format warnings to errors. (#GH211943) +- Fixed a crash when `__atomic_always_lock_free` or `__atomic_is_lock_free` is + called with a size of zero. (#GH170139, #GH120082) - Fixed wrong code generation in `__builtin_clear_padding` wherein the wrong bits of the following types were cleared: `_BitInt`, struct bitfields, and packed boolean vectors. (#GH215809), (#GH216063), (#GH224033) diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 5ea3cc6bd2920..48b896e2ccece 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1181,7 +1181,7 @@ static bool interp__builtin_atomic_lock_free(InterpState &S, CodePtr OpPC, // Check power-of-two. CharUnits Size = CharUnits::fromQuantity(SizeVal); - if (Size.isPowerOfTwo()) { + if (Size.isPositive() && Size.isPowerOfTwo()) { // Check against inlining width. unsigned InlineWidthBits = S.getASTContext().getTargetInfo().getMaxAtomicInlineWidth(); @@ -1239,7 +1239,7 @@ static bool interp__builtin_c11_atomic_is_lock_free(InterpState &S, return false; CharUnits Size = CharUnits::fromQuantity(SizeVal); - if (Size.isPowerOfTwo()) { + if (Size.isPositive() && Size.isPowerOfTwo()) { // Check against inlining width. unsigned InlineWidthBits = S.getASTContext().getTargetInfo().getMaxAtomicInlineWidth(); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 34c3e39807c7f..98ed45ceaa0fa 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -18084,7 +18084,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, // Check power-of-two. CharUnits Size = CharUnits::fromQuantity(SizeVal.getZExtValue()); - if (Size.isPowerOfTwo()) { + if (Size.isPositive() && Size.isPowerOfTwo()) { // Check against inlining width. unsigned InlineWidthBits = Info.Ctx.getTargetInfo().getMaxAtomicInlineWidth(); diff --git a/clang/test/AST/ByteCode/atomic.c b/clang/test/AST/ByteCode/atomic.c index a93787ccd1e02..ff85c98a0f7b7 100644 --- a/clang/test/AST/ByteCode/atomic.c +++ b/clang/test/AST/ByteCode/atomic.c @@ -65,9 +65,23 @@ _Static_assert(!__atomic_always_lock_free(4, (void*)2), ""); _Static_assert(!__atomic_always_lock_free(4, (void*)-2), ""); _Static_assert(__atomic_always_lock_free(4, (void*)4), ""); _Static_assert(__atomic_always_lock_free(4, (void*)-4), ""); +_Static_assert(!__atomic_always_lock_free(2, (void*)1), ""); +_Static_assert(__atomic_always_lock_free(2, (void*)2), ""); +_Static_assert(!__atomic_always_lock_free(8, (void*)4), ""); +_Static_assert(__atomic_always_lock_free(8, (void*)8), ""); _Static_assert(__atomic_always_lock_free(1, "string"), ""); _Static_assert(!__atomic_always_lock_free(2, "string"), ""); _Static_assert(__atomic_always_lock_free(2, (int[2]){}), ""); void dummyfn(); _Static_assert(__atomic_always_lock_free(2, dummyfn) || 1, ""); + +// GH170139, GH120082 +_Static_assert(!__atomic_always_lock_free(0, 0), ""); +_Static_assert(!__atomic_always_lock_free(0, 1), ""); // both-error {{incompatible integer to pointer conversion}} +_Static_assert(!__atomic_always_lock_free(0, (void*)4), ""); +_Static_assert(!__atomic_always_lock_free(0, &ai), ""); +_Static_assert(__atomic_is_lock_free(0, 0), ""); // both-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(0, (void*)4), ""); // both-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(0, &ai), ""); // both-error {{not an integral constant expression}} +_Static_assert(__c11_atomic_is_lock_free(0), ""); // both-error {{not an integral constant expression}} diff --git a/clang/test/CodeGen/atomic-ops.c b/clang/test/CodeGen/atomic-ops.c index 8716d3aeb9516..c2318c8512956 100644 --- a/clang/test/CodeGen/atomic-ops.c +++ b/clang/test/CodeGen/atomic-ops.c @@ -370,6 +370,22 @@ int lock_free(struct Incomplete *incomplete) { return __c11_atomic_is_lock_free(sizeof(_Atomic(int))); } +int gh170139(void) { + // CHECK-LABEL: @gh170139 + + // CHECK: call zeroext i1 @__atomic_is_lock_free(i32 noundef 0, ptr noundef null) + __atomic_is_lock_free(0, 0); + + // CHECK: call zeroext i1 @__atomic_is_lock_free(i32 noundef 0, ptr noundef null) + __c11_atomic_is_lock_free(0); + + // CHECK-NOT: call + __atomic_always_lock_free(0, 0); + + // CHECK: ret i32 0 + return __atomic_always_lock_free(0, 0); +} + // Tests for atomic operations on big values. These should call the functions // defined here: // http://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary#The_Library_interface diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c index a3bc8d2ec75e7..cebbc78ea2262 100644 --- a/clang/test/Sema/atomic-ops.c +++ b/clang/test/Sema/atomic-ops.c @@ -147,6 +147,18 @@ _Static_assert(__atomic_always_lock_free(2, (int[2]){}), ""); void dummyfn(); _Static_assert(__atomic_always_lock_free(2, dummyfn) || 1, ""); +// GH170139, GH120082 +_Static_assert(!__atomic_always_lock_free(0, 0), ""); +_Static_assert(!__atomic_always_lock_free(0, 1), ""); // expected-error {{incompatible integer to pointer conversion}} +_Static_assert(!__atomic_always_lock_free(0, (void*)4), ""); +_Static_assert(!__atomic_always_lock_free(0, &i32), ""); +_Static_assert(__atomic_is_lock_free(0, 0), ""); // expected-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(0, (void*)4), ""); // expected-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(0, &i32), ""); // expected-error {{not an integral constant expression}} +_Static_assert(__c11_atomic_is_lock_free(0), ""); // expected-error {{not an integral constant expression}} +_Static_assert(!__atomic_always_lock_free(SIZE_MAX / 2 + 1, 0), ""); +_Static_assert(__c11_atomic_is_lock_free(SIZE_MAX / 2 + 1), ""); // expected-error {{not an integral constant expression}} + #define _AS1 __attribute__((address_space(1))) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
