[clang] 8e965d8 - [analyzer] Fix zext assertion failure in loop unrolling (#121203)

via cfe-commits cfe-commits at lists.llvm.org
Sat Dec 28 02:09:33 PST 2024


Author: JOSTAR
Date: 2024-12-28T11:09:29+01:00
New Revision: 8e965d89c9624c184c48806dc39d50209265f0f8

URL: https://github.com/llvm/llvm-project/commit/8e965d89c9624c184c48806dc39d50209265f0f8
DIFF: https://github.com/llvm/llvm-project/commit/8e965d89c9624c184c48806dc39d50209265f0f8.diff

LOG: [analyzer] Fix zext assertion failure in loop unrolling (#121203)

The current implementation of APInt extension in the code can trigger an
assertion failure when the `zext` function is called with a target width
smaller than the current bit width. For example:
```cpp
if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
    InitNum = InitNum.zext(BoundNum.getBitWidth());
    BoundNum = BoundNum.zext(InitNum.getBitWidth());
}
```

This logic does not guarantee that the `zext` target width is always
greater than or equal to the current bit width, leading to potential
crashes.

Expected Behavior:
- Ensure InitNum and BoundNum are extended to the maximum of their respective widths.
- Prevent assertion failures by enforcing correct `zext` usage.

Fixes #121201

Added: 
    clang/test/Analysis/PR121201.cpp

Modified: 
    clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index 96f5d7c44baf89..01d87b02fcdbd0 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -283,10 +283,10 @@ static bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
   llvm::APInt InitNum =
       Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue();
   auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator");
-  if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
-    InitNum = InitNum.zext(BoundNum.getBitWidth());
-    BoundNum = BoundNum.zext(InitNum.getBitWidth());
-  }
+  unsigned MaxWidth = std::max(InitNum.getBitWidth(), BoundNum.getBitWidth());
+
+  InitNum = InitNum.zext(MaxWidth);
+  BoundNum = BoundNum.zext(MaxWidth);
 
   if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE)
     maxStep = (BoundNum - InitNum + 1).abs().getZExtValue();

diff  --git a/clang/test/Analysis/PR121201.cpp b/clang/test/Analysis/PR121201.cpp
new file mode 100644
index 00000000000000..acd2492d011fad
--- /dev/null
+++ b/clang/test/Analysis/PR121201.cpp
@@ -0,0 +1,67 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s \
+// RUN:    -analyzer-config unroll-loops=true
+
+// expected-no-diagnostics
+
+template <bool, typename T, typename> using conditional_t = T;
+class basic_format_arg;
+template <typename> struct formatter;
+
+template <typename Context> struct value {
+  template <typename T> value(T) {
+    using value_type = T;
+    (void)format_custom_arg<value_type,
+                      typename Context::template formatter_type<value_type>>;
+  }
+
+  template <typename, typename Formatter> static void format_custom_arg() {
+    Context ctx;
+    auto f = Formatter();
+    f.format(0, ctx);
+  }
+};
+
+struct context {
+  template <typename T> using formatter_type = formatter<T>;
+};
+
+enum { max_packed_args };
+
+template <typename Context, long>
+using arg_t = conditional_t<max_packed_args, value<Context>, basic_format_arg>;
+
+template <int NUM_ARGS> struct format_arg_store {
+  arg_t<context, NUM_ARGS> args;
+};
+
+template <typename... T, long NUM_ARGS = sizeof...(T)>
+auto make_format_args(T... args) -> format_arg_store<NUM_ARGS> {
+  return {args...};
+}
+
+template <typename F> void write_padded(F write) { write(0); }
+
+template <typename... T> void format(T... args) { make_format_args(args...); }
+
+template <int> struct bitset {
+  bitset(long);
+};
+
+template <long N> struct formatter<bitset<N>> {
+  struct writer {
+    bitset<N> bs;
+
+    template <typename OutputIt> void operator()(OutputIt) {
+      for (auto pos = N; pos > 0; --pos) // no-crash
+        ;
+    }
+  };
+
+  template <typename FormatContext> void format(bitset<N> bs, FormatContext) {
+    write_padded(writer{bs});
+  }
+};
+
+bitset<6> TestBody_bs(2);
+
+void TestBody() { format(TestBody_bs); }


        


More information about the cfe-commits mailing list