Skip to content

[Sema] Handle large shift counts in GetExprRange #68590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 10, 2023
Merged

Conversation

bjope
Copy link
Collaborator

@bjope bjope commented Oct 9, 2023

GetExprRange did not expect that very large shift counts when
narrowing the range based on logical right shifts. So with inputs
such as
*a >> 123456789012345678901uwb
it would hit assertions about trying to convert a too large APInt
into uint64_t.
This patch fixes that by using the APInt value when determining if
we should reduce the range by the shift count or not.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Oct 9, 2023
@llvmbot
Copy link
Member

llvmbot commented Oct 9, 2023

@llvm/pr-subscribers-clang

Changes

GetExprRange did not expect that very large shift counts when
narrowing the range based on logical right shifts. So with inputs
such as
*a >> 123456789012345678901uwb
it would hit assertions about trying to convert a too large APInt
into uint64_t.
This patch fixes that by using the APInt value when determining if
we should reduce the range by the shift count or not.


Full diff: https://github.com/llvm/llvm-project/pull/68590.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaChecking.cpp (+2-3)
  • (added) clang/test/Sema/c2x-expr-range.c (+20)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 3932d9cd07d9864..eb4d8d2e2806bcb 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -13554,11 +13554,10 @@ static IntRange GetExprRange(ASTContext &C, const Expr *E, unsigned MaxWidth,
       if (std::optional<llvm::APSInt> shift =
               BO->getRHS()->getIntegerConstantExpr(C)) {
         if (shift->isNonNegative()) {
-          unsigned zext = shift->getZExtValue();
-          if (zext >= L.Width)
+          if (shift->uge(L.Width))
             L.Width = (L.NonNegative ? 0 : 1);
           else
-            L.Width -= zext;
+            L.Width -= shift->getZExtValue();
         }
       }
 
diff --git a/clang/test/Sema/c2x-expr-range.c b/clang/test/Sema/c2x-expr-range.c
new file mode 100644
index 000000000000000..1690a6280386bd5
--- /dev/null
+++ b/clang/test/Sema/c2x-expr-range.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -std=c2x -triple=x86_64-unknown-linux %s
+
+// test1 used to hit an assertion failure like this during parsing:
+// clang: ../include/llvm/ADT/APInt.h:1488: uint64_t llvm::APInt::getZExtValue() const: Assertion `getActiveBits() <= 64 && "Too many bits for uint64_t"' failed.
+// With a stack trace like this leading up to the assert:
+//
+//  #9 0x00005645bca16518 GetExprRange(clang::ASTContext&, clang::Expr const*, unsigned int, bool, bool) SemaChecking.cpp:0:0
+// #10 0x00005645bca048a8 AnalyzeImplicitConversions(clang::Sema&, clang::Expr*, clang::SourceLocation, bool) SemaChecking.cpp:0:0
+// #11 0x00005645bca06af1 clang::Sema::CheckCompletedExpr(clang::Expr*, clang::SourceLocation, bool)
+//
+void test1(int *a)
+{
+  (void)(*a >> 123456789012345678901uwb <= 0); // expected-warning {{shift count >= width of type}}
+}
+
+// Same as above but using __uint128_t instead of __BitInt.
+void test2(__uint128_t *a)
+{
+  (void)(*a >> ((__uint128_t)__UINT64_MAX__ + (__uint128_t)1) <= 0); // expected-warning {{shift count >= width of type}}
+}

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the change itself looks right. The comment at the top of the test belongs in the commit message/github discussion, not in the test itself.

Also, needs a release note.

@bjope
Copy link
Collaborator Author

bjope commented Oct 9, 2023

So the change itself looks right. The comment at the top of the test belongs in the commit message/github discussion, not in the test itself.

Also, needs a release note.

Thanks. I pushed an update. It fixes the test case to just explain the purpose of the tests. And I added info to the ReleaseNotes.

Note: I've never edited release notes for clang in the past, so please let me know if that should go somewhere else or if I missed something about what it should look like, etc.

@@ -219,6 +219,10 @@ Bug Fixes in This Version
- Clang no longer considers the loss of ``__unaligned`` qualifier from objects as
an invalid conversion during method function overload resolution.

- Fixed an issue when a shift count larger than ``__INT64_MAX__``, in a right
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue filed in the issue tracker for this bug?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue filed in the issue tracker for this bug?

Not that I'm aware of. Problem was found in downstream fuzzy testing involving BitInt types.

@@ -0,0 +1,16 @@
// RUN: %clang_cc1 -verify -fsyntax-only -std=c2x -triple=x86_64-unknown-linux %s

// Regression test for bug where we used to hit assertion due to shift amount
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Regression test for bug where we used to hit assertion due to shift amount
// Regression test for bug where we used to hit an assertion due to shift amount

// Regression test for bug where we used to hit assertion due to shift amount
// being larger than 64 bits. We want to see a warning about too large shift
// amount.
void test1(int *a)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curleys have to be on the same line as the declaration here by coding standard.


// Similar to test1 above, but using __uint128_t instead of __BitInt.
// We want to see a warning about too large shift amount.
void test2(__uint128_t *a)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

// We want to see a warning about too large shift amount.
void test2(__uint128_t *a)
{
(void)(*a >> ((__uint128_t)__UINT64_MAX__ + (__uint128_t)1) <= 0); // expected-warning {{shift count >= width of type}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2nd uint128_t cast shouldn't be necessary

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

GetExprRange did not expect that very large shift counts when
narrowing the range based on logical right shifts. So with inputs
such as
    *a >> 123456789012345678901uwb
it would hit assertions about trying to convert a too large APInt
into uint64_t.
This patch fixes that by using the APInt value when determining if
we should reduce the range by the shift count or not.
@bjope bjope merged commit 7d2b569 into llvm:main Oct 10, 2023
@bjope bjope deleted the sema branch March 20, 2024 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants