Skip to content

Commit

Permalink
[analyzer] Fix zext assertion failure in loop unrolling (#121203)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
shenjunjiekoda authored Dec 28, 2024
1 parent f2f02b2 commit 8e965d8
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 4 deletions.
8 changes: 4 additions & 4 deletions clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
67 changes: 67 additions & 0 deletions clang/test/Analysis/PR121201.cpp
Original file line number Diff line number Diff line change
@@ -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); }

0 comments on commit 8e965d8

Please sign in to comment.