Skip to content

Commit 8e965d8

Browse files
[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
1 parent f2f02b2 commit 8e965d8

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,10 @@ static bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
283283
llvm::APInt InitNum =
284284
Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue();
285285
auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator");
286-
if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
287-
InitNum = InitNum.zext(BoundNum.getBitWidth());
288-
BoundNum = BoundNum.zext(InitNum.getBitWidth());
289-
}
286+
unsigned MaxWidth = std::max(InitNum.getBitWidth(), BoundNum.getBitWidth());
287+
288+
InitNum = InitNum.zext(MaxWidth);
289+
BoundNum = BoundNum.zext(MaxWidth);
290290

291291
if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE)
292292
maxStep = (BoundNum - InitNum + 1).abs().getZExtValue();

clang/test/Analysis/PR121201.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s \
2+
// RUN: -analyzer-config unroll-loops=true
3+
4+
// expected-no-diagnostics
5+
6+
template <bool, typename T, typename> using conditional_t = T;
7+
class basic_format_arg;
8+
template <typename> struct formatter;
9+
10+
template <typename Context> struct value {
11+
template <typename T> value(T) {
12+
using value_type = T;
13+
(void)format_custom_arg<value_type,
14+
typename Context::template formatter_type<value_type>>;
15+
}
16+
17+
template <typename, typename Formatter> static void format_custom_arg() {
18+
Context ctx;
19+
auto f = Formatter();
20+
f.format(0, ctx);
21+
}
22+
};
23+
24+
struct context {
25+
template <typename T> using formatter_type = formatter<T>;
26+
};
27+
28+
enum { max_packed_args };
29+
30+
template <typename Context, long>
31+
using arg_t = conditional_t<max_packed_args, value<Context>, basic_format_arg>;
32+
33+
template <int NUM_ARGS> struct format_arg_store {
34+
arg_t<context, NUM_ARGS> args;
35+
};
36+
37+
template <typename... T, long NUM_ARGS = sizeof...(T)>
38+
auto make_format_args(T... args) -> format_arg_store<NUM_ARGS> {
39+
return {args...};
40+
}
41+
42+
template <typename F> void write_padded(F write) { write(0); }
43+
44+
template <typename... T> void format(T... args) { make_format_args(args...); }
45+
46+
template <int> struct bitset {
47+
bitset(long);
48+
};
49+
50+
template <long N> struct formatter<bitset<N>> {
51+
struct writer {
52+
bitset<N> bs;
53+
54+
template <typename OutputIt> void operator()(OutputIt) {
55+
for (auto pos = N; pos > 0; --pos) // no-crash
56+
;
57+
}
58+
};
59+
60+
template <typename FormatContext> void format(bitset<N> bs, FormatContext) {
61+
write_padded(writer{bs});
62+
}
63+
};
64+
65+
bitset<6> TestBody_bs(2);
66+
67+
void TestBody() { format(TestBody_bs); }

0 commit comments

Comments
 (0)