26#include "llvm/Support/FormatVariadic.h"
36using BugReportPtr = std::unique_ptr<PathSensitiveBugReport>;
38struct NoteTagTemplate {
39 llvm::StringLiteral SignInfo;
40 llvm::StringLiteral UpperBoundIntro;
43constexpr NoteTagTemplate NoteTagTemplates[] = {
44 {
"",
"right operand of bit shift is less than "},
45 {
"left operand of bit shift is non-negative",
" and right operand is less than "},
46 {
"right operand of bit shift is non-negative",
" but less than "},
47 {
"both operands of bit shift are non-negative",
" and right operand is less than "}
53class BitwiseShiftValidator {
58 const bool PedanticFlag;
61 enum { NonNegLeft = 1, NonNegRight = 2 };
62 unsigned NonNegOperands = 0;
64 std::optional<unsigned> UpperBoundBitCount = std::nullopt;
69 : Ctx(
C), FoldedState(
C.getState()), Op(O), BT(B), PedanticFlag(
P) {}
73 const Expr *operandExpr(OperandSide Side)
const {
74 return Side == OperandSide::Left ? Op->
getLHS() : Op->
getRHS();
77 bool shouldPerformPedanticChecks()
const {
86 const NoteTag *createNoteTag()
const;
88 BugReportPtr createBugReport(StringRef ShortMsg, StringRef Msg)
const;
90 BugReportPtr checkOvershift();
91 BugReportPtr checkOperandNegative(OperandSide Side);
92 BugReportPtr checkLeftShiftOverflow();
94 bool isLeftShift()
const {
return Op->
getOpcode() == BO_Shl; }
95 StringRef shiftDir()
const {
return isLeftShift() ?
"left" :
"right"; }
96 static StringRef pluralSuffix(
unsigned n) {
return n <= 1 ?
"" :
"s"; }
97 static StringRef verbSuffix(
unsigned n) {
return n <= 1 ?
"s" :
""; }
100void BitwiseShiftValidator::run() {
103 if (BugReportPtr BR = checkOvershift()) {
109 if (BugReportPtr BR = checkOperandNegative(OperandSide::Right)) {
114 if (shouldPerformPedanticChecks()) {
116 if (BugReportPtr BR = checkOperandNegative(OperandSide::Left)) {
122 if (BugReportPtr BR = checkLeftShiftOverflow()) {
137bool BitwiseShiftValidator::assumeRequirement(OperandSide Side,
142 const SVal OperandVal = Ctx.
getSVal(operandExpr(Side));
147 auto ResultVal = SVB.
evalBinOp(FoldedState, Comparison, OperandVal, LimitVal,
150 auto [StTrue, StFalse] = FoldedState->assume(DURes.value());
153 FoldedState = StFalse;
157 FoldedState = StTrue;
160 recordAssumption(Side, Comparison, Limit);
166BugReportPtr BitwiseShiftValidator::checkOvershift() {
170 if (assumeRequirement(OperandSide::Right, BO_LT, LHSBitWidth))
175 std::string RightOpStr =
"", LowerBoundStr =
"";
177 RightOpStr = formatv(
" '{0}'", ConcreteRight->getValue());
180 if (
const llvm::APSInt *MinRight = SVB.
getMinValue(FoldedState, Right);
181 MinRight && *MinRight >= LHSBitWidth) {
182 LowerBoundStr = formatv(
" >= {0},", MinRight->getExtValue());
186 std::string ShortMsg = formatv(
187 "{0} shift{1}{2} overflows the capacity of '{3}'",
188 isLeftShift() ?
"Left" :
"Right", RightOpStr.empty() ?
"" :
" by",
190 std::string Msg = formatv(
191 "The result of {0} shift is undefined because the right "
192 "operand{1} is{2} not smaller than {3}, the capacity of '{4}'",
193 shiftDir(), RightOpStr, LowerBoundStr, LHSBitWidth, LHSTy.
getAsString());
194 return createBugReport(ShortMsg, Msg);
207BugReportPtr BitwiseShiftValidator::checkOperandNegative(OperandSide Side) {
209 if (!operandExpr(Side)->getType()->isSignedIntegerType())
213 if (assumeRequirement(Side, BO_GE, 0))
216 std::string ShortMsg = formatv(
"{0} operand is negative in {1} shift",
217 Side == OperandSide::Left ?
"Left" :
"Right",
220 std::string Msg = formatv(
"The result of {0} shift is undefined "
221 "because the {1} operand is negative",
223 Side == OperandSide::Left ?
"left" :
"right")
226 return createBugReport(ShortMsg, Msg);
229BugReportPtr BitwiseShiftValidator::checkLeftShiftOverflow() {
236 const bool ShouldPreserveSignBit = !Ctx.
getLangOpts().CPlusPlus;
238 const Expr *LHS = operandExpr(OperandSide::Left);
241 assert(LeftBitWidth > 0);
250 if (!
Left.has_value())
255 assert(
Left->getValue()->isNonNegative());
257 const unsigned LeftAvailableBitWidth =
258 LeftBitWidth -
static_cast<unsigned>(ShouldPreserveSignBit);
259 const unsigned UsedBitsInLeftOperand =
Left->getValue()->getActiveBits();
260 assert(LeftBitWidth >= UsedBitsInLeftOperand);
261 const unsigned MaximalAllowedShift =
262 LeftAvailableBitWidth - UsedBitsInLeftOperand;
264 if (assumeRequirement(OperandSide::Right, BO_LT, MaximalAllowedShift + 1))
267 const std::string CapacityMsg =
268 formatv(
"because '{0}' can hold only {1} bits ({2} the sign bit)",
270 ShouldPreserveSignBit ?
"excluding" :
"including");
274 std::string ShortMsg, Msg;
278 const int64_t RHS = ConcreteRight->getValue()->getExtValue();
279 assert(RHS > MaximalAllowedShift);
280 const int64_t OverflownBits = RHS - MaximalAllowedShift;
282 "The shift '{0} << {1}' overflows the capacity of '{2}'",
285 "The shift '{0} << {1}' is undefined {2}, so {3} bit{4} overflow{5}",
286 Left->getValue(), ConcreteRight->getValue(), CapacityMsg, OverflownBits,
287 pluralSuffix(OverflownBits), verbSuffix(OverflownBits));
289 ShortMsg = formatv(
"Left shift of '{0}' overflows the capacity of '{1}'",
292 "Left shift of '{0}' is undefined {1}, so some bits overflow",
293 Left->getValue(), CapacityMsg);
296 return createBugReport(ShortMsg, Msg);
299void BitwiseShiftValidator::recordAssumption(OperandSide Side,
302 switch (Comparison) {
305 NonNegOperands |= (Side == OperandSide::Left ? NonNegLeft : NonNegRight);
308 assert(Side == OperandSide::Right);
309 if (!UpperBoundBitCount || Limit < UpperBoundBitCount.value())
310 UpperBoundBitCount = Limit;
313 llvm_unreachable(
"this checker does not use other comparison operators");
317const NoteTag *BitwiseShiftValidator::createNoteTag()
const {
318 if (!NonNegOperands && !UpperBoundBitCount)
322 llvm::raw_svector_ostream Out(Buf);
324 NoteTagTemplate Templ = NoteTagTemplates[NonNegOperands];
325 Out << Templ.SignInfo;
326 if (UpperBoundBitCount)
327 Out << Templ.UpperBoundIntro << UpperBoundBitCount.value();
328 const std::string Msg(Out.str());
333std::unique_ptr<PathSensitiveBugReport>
334BitwiseShiftValidator::createBugReport(StringRef ShortMsg, StringRef Msg)
const {
338 std::make_unique<PathSensitiveBugReport>(BT, ShortMsg, Msg, ErrNode);
348 BugType BT{
this,
"Bitwise shift",
"Suspicious operation"};
354 if (Op != BO_Shl && Op != BO_Shr)
357 BitwiseShiftValidator(B, Ctx, BT,
Pedantic).run();
369bool ento::shouldRegisterBitwiseShiftChecker(
const CheckerManager &mgr) {
Defines the clang::ASTContext interface.
void checkPreStmt(const BinaryOperator *B, CheckerContext &Ctx) const
unsigned getIntWidth(QualType T) const
const LangOptions & getLangOpts() const
Stores options for the analyzer from the command line.
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
A builtin binary operation expression such as "x + y" or "x <= y".
This represents one expression.
A (possibly-)qualified type.
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)
Used to register checkers.
The tag upon which the TagVisitor reacts.
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)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Value representing integer constant.
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.
The JSON file list parser is used to communicate input to InstallAPI.