24#include "llvm/Support/FormatVariadic.h"
32enum class OperandSide { Left, Right };
34using BugReportPtr = std::unique_ptr<PathSensitiveBugReport>;
36struct NoteTagTemplate {
37 llvm::StringLiteral SignInfo;
38 llvm::StringLiteral UpperBoundIntro;
41constexpr NoteTagTemplate NoteTagTemplates[] = {
42 {
"",
"right operand of bit shift is less than "},
43 {
"left operand of bit shift is non-negative",
" and right operand is less than "},
44 {
"right operand of bit shift is non-negative",
" but less than "},
45 {
"both operands of bit shift are non-negative",
" and right operand is less than "}
51class BitwiseShiftValidator {
54 const BinaryOperator *
const Op;
56 const bool PedanticFlag;
59 enum { NonNegLeft = 1, NonNegRight = 2 };
60 unsigned NonNegOperands = 0;
62 std::optional<unsigned> UpperBoundBitCount = std::nullopt;
65 BitwiseShiftValidator(
const BinaryOperator *O, CheckerContext &
C,
66 const BugType &B,
bool P)
67 : Ctx(
C), FoldedState(
C.getState()), Op(O), BT(B), PedanticFlag(P) {}
71 const Expr *operandExpr(OperandSide Side)
const {
72 return Side == OperandSide::Left ? Op->getLHS() : Op->getRHS();
75 bool shouldPerformPedanticChecks()
const {
78 return PedanticFlag && !Ctx.getASTContext().getLangOpts().CPlusPlus20;
84 const NoteTag *createNoteTag()
const;
86 BugReportPtr createBugReport(StringRef ShortMsg, StringRef Msg)
const;
88 BugReportPtr checkOvershift();
89 BugReportPtr checkOperandNegative(OperandSide Side);
90 BugReportPtr checkLeftShiftOverflow();
92 bool isLeftShift()
const {
return Op->getOpcode() == BO_Shl; }
93 StringRef shiftDir()
const {
return isLeftShift() ?
"left" :
"right"; }
94 static StringRef pluralSuffix(
unsigned n) {
return n <= 1 ?
"" :
"s"; }
95 static StringRef verbSuffix(
unsigned n) {
return n <= 1 ?
"s" :
""; }
98void BitwiseShiftValidator::run() {
101 if (BugReportPtr BR = checkOvershift()) {
107 if (BugReportPtr BR = checkOperandNegative(OperandSide::Right)) {
112 if (shouldPerformPedanticChecks()) {
114 if (BugReportPtr BR = checkOperandNegative(OperandSide::Left)) {
120 if (BugReportPtr BR = checkLeftShiftOverflow()) {
135bool BitwiseShiftValidator::assumeRequirement(OperandSide Side,
140 const SVal OperandVal = Ctx.
getSVal(operandExpr(Side));
147 if (
auto DURes = ResultVal.getAs<DefinedOrUnknownSVal>()) {
148 auto [StTrue, StFalse] = FoldedState->assume(DURes.value());
151 FoldedState = StFalse;
155 FoldedState = StTrue;
164BugReportPtr BitwiseShiftValidator::checkOvershift() {
168 if (assumeRequirement(OperandSide::Right, BO_LT, LHSBitWidth))
171 const SVal
Right = Ctx.
getSVal(operandExpr(OperandSide::Right));
173 std::string RightOpStr =
"", LowerBoundStr =
"";
174 if (
auto ConcreteRight =
Right.getAs<nonloc::ConcreteInt>())
175 RightOpStr = formatv(
" '{0}'", ConcreteRight->getValue());
178 if (
const llvm::APSInt *MinRight = SVB.
getMinValue(FoldedState, Right);
179 MinRight && *MinRight >= LHSBitWidth) {
180 LowerBoundStr = formatv(
" >= {0},", MinRight->getExtValue());
184 std::string ShortMsg = formatv(
185 "{0} shift{1}{2} overflows the capacity of '{3}'",
186 isLeftShift() ?
"Left" :
"Right", RightOpStr.empty() ?
"" :
" by",
188 std::string Msg = formatv(
189 "The result of {0} shift is undefined because the right "
190 "operand{1} is{2} not smaller than {3}, the capacity of '{4}'",
191 shiftDir(), RightOpStr, LowerBoundStr, LHSBitWidth, LHSTy.
getAsString());
192 return createBugReport(ShortMsg, Msg);
205BugReportPtr BitwiseShiftValidator::checkOperandNegative(OperandSide Side) {
207 if (!operandExpr(Side)->
getType()->isSignedIntegerType())
211 if (assumeRequirement(Side, BO_GE, 0))
214 std::string ShortMsg = formatv(
"{0} operand is negative in {1} shift",
215 Side == OperandSide::Left ?
"Left" :
"Right",
218 std::string Msg = formatv(
"The result of {0} shift is undefined "
219 "because the {1} operand is negative",
221 Side == OperandSide::Left ?
"left" :
"right")
224 return createBugReport(ShortMsg, Msg);
227BugReportPtr BitwiseShiftValidator::checkLeftShiftOverflow() {
234 const bool ShouldPreserveSignBit = !Ctx.
getLangOpts().CPlusPlus;
236 const Expr *LHS = operandExpr(OperandSide::Left);
237 const QualType LHSTy = LHS->
getType();
239 assert(LeftBitWidth > 0);
248 if (!
Left.has_value())
253 assert(
Left->getValue()->isNonNegative());
255 const unsigned LeftAvailableBitWidth =
256 LeftBitWidth -
static_cast<unsigned>(ShouldPreserveSignBit);
257 const unsigned UsedBitsInLeftOperand =
Left->getValue()->getActiveBits();
258 assert(LeftBitWidth >= UsedBitsInLeftOperand);
259 const unsigned MaximalAllowedShift =
260 LeftAvailableBitWidth - UsedBitsInLeftOperand;
262 if (assumeRequirement(OperandSide::Right, BO_LT, MaximalAllowedShift + 1))
265 const std::string CapacityMsg =
266 formatv(
"because '{0}' can hold only {1} bits ({2} the sign bit)",
268 ShouldPreserveSignBit ?
"excluding" :
"including");
272 std::string ShortMsg, Msg;
273 if (
const auto ConcreteRight =
Right.getAs<nonloc::ConcreteInt>()) {
276 const int64_t RHS = ConcreteRight->getValue()->getExtValue();
277 assert(RHS > MaximalAllowedShift);
278 const int64_t OverflownBits = RHS - MaximalAllowedShift;
280 "The shift '{0} << {1}' overflows the capacity of '{2}'",
283 "The shift '{0} << {1}' is undefined {2}, so {3} bit{4} overflow{5}",
284 Left->getValue(), ConcreteRight->getValue(), CapacityMsg, OverflownBits,
285 pluralSuffix(OverflownBits), verbSuffix(OverflownBits));
287 ShortMsg = formatv(
"Left shift of '{0}' overflows the capacity of '{1}'",
290 "Left shift of '{0}' is undefined {1}, so some bits overflow",
291 Left->getValue(), CapacityMsg);
294 return createBugReport(ShortMsg, Msg);
297void BitwiseShiftValidator::recordAssumption(OperandSide Side,
303 NonNegOperands |= (Side == OperandSide::Left ? NonNegLeft : NonNegRight);
306 assert(Side == OperandSide::Right);
307 if (!UpperBoundBitCount || Limit < UpperBoundBitCount.value())
308 UpperBoundBitCount = Limit;
311 llvm_unreachable(
"this checker does not use other comparison operators");
315const NoteTag *BitwiseShiftValidator::createNoteTag()
const {
316 if (!NonNegOperands && !UpperBoundBitCount)
319 SmallString<128> Buf;
320 llvm::raw_svector_ostream
Out(Buf);
322 NoteTagTemplate Templ = NoteTagTemplates[NonNegOperands];
323 Out << Templ.SignInfo;
324 if (UpperBoundBitCount)
325 Out << Templ.UpperBoundIntro << UpperBoundBitCount.value();
326 const std::string Msg(
Out.str());
331std::unique_ptr<PathSensitiveBugReport>
332BitwiseShiftValidator::createBugReport(StringRef ShortMsg, StringRef Msg)
const {
336 std::make_unique<PathSensitiveBugReport>(BT, ShortMsg, Msg, ErrNode);
346 BugType BT{
this,
"Bitwise shift",
"Suspicious operation"};
352 if (Op != BO_Shl && Op != BO_Shr)
355 BitwiseShiftValidator(B, Ctx, BT,
Pedantic).run();
364 Chk->Pedantic = Opts.getCheckerBooleanOption(Chk,
"Pedantic");
367bool ento::shouldRegisterBitwiseShiftChecker(
const CheckerManager &mgr) {
Defines the clang::ASTContext interface.
void checkPreStmt(const BinaryOperator *B, CheckerContext &Ctx) const
unsigned getIntWidth(QualType T) const
Stores options for the analyzer from the command line.
A builtin binary operation expression such as "x + y" or "x <= y".
BinaryOperatorKind Opcode
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
SValBuilder & getSValBuilder()
ASTContext & getASTContext()
const ProgramStateRef & getState() const
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.
LLVM_ATTRIBUTE_RETURNS_NONNULL const NoteTag * getNoteTag(NoteTag::Callback &&Cb, bool IsPrunable=false)
Produce a program point tag that displays an additional path note to the user.
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
const LangOptions & getLangOpts() const
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
virtual const llvm::APSInt * getMinValue(ProgramStateRef state, SVal val)=0
Tries to get the minimal possible (integer) value of a given SVal.
QualType getConditionType() const
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
The JSON file list parser is used to communicate input to InstallAPI.
@ Comparison
A comparison.