1 //== BitwiseShiftChecker.cpp ------------------------------------*- C++ -*--==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines BitwiseShiftChecker, which is a path-sensitive checker 10 // that looks for undefined behavior when the operands of the bitwise shift 11 // operators '<<' and '>>' are invalid (negative or too large). 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/AST/ASTContext.h" 16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 24 #include "llvm/Support/FormatVariadic.h" 25 #include <memory> 26 27 using namespace clang; 28 using namespace ento; 29 using llvm::formatv; 30 31 namespace { 32 enum class OperandSide { Left, Right }; 33 34 using BugReportPtr = std::unique_ptr<PathSensitiveBugReport>; 35 36 struct NoteTagTemplate { 37 llvm::StringLiteral SignInfo; 38 llvm::StringLiteral UpperBoundIntro; 39 }; 40 41 constexpr 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 "} 46 }; 47 48 /// An implementation detail class which is introduced to split the checker 49 /// logic into several methods while maintaining a consistently updated state 50 /// and access to other contextual data. 51 class BitwiseShiftValidator { 52 CheckerContext &Ctx; 53 ProgramStateRef FoldedState; 54 const BinaryOperator *const Op; 55 const BugType &BT; 56 const bool PedanticFlag; 57 58 // The following data members are only used for note tag creation: 59 enum { NonNegLeft = 1, NonNegRight = 2 }; 60 unsigned NonNegOperands = 0; 61 62 std::optional<unsigned> UpperBoundBitCount = std::nullopt; 63 64 public: 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) {} 68 void run(); 69 70 private: 71 const Expr *operandExpr(OperandSide Side) const { 72 return Side == OperandSide::Left ? Op->getLHS() : Op->getRHS(); 73 } 74 75 bool shouldPerformPedanticChecks() const { 76 // The pedantic flag has no effect under C++20 because the affected issues 77 // are no longer undefined under that version of the standard. 78 return PedanticFlag && !Ctx.getASTContext().getLangOpts().CPlusPlus20; 79 } 80 81 bool assumeRequirement(OperandSide Side, BinaryOperator::Opcode Cmp, unsigned Limit); 82 83 void recordAssumption(OperandSide Side, BinaryOperator::Opcode Cmp, unsigned Limit); 84 const NoteTag *createNoteTag() const; 85 86 BugReportPtr createBugReport(StringRef ShortMsg, StringRef Msg) const; 87 88 BugReportPtr checkOvershift(); 89 BugReportPtr checkOperandNegative(OperandSide Side); 90 BugReportPtr checkLeftShiftOverflow(); 91 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" : ""; } 96 }; 97 98 void BitwiseShiftValidator::run() { 99 // Report a bug if the right operand is >= the bit width of the type of the 100 // left operand: 101 if (BugReportPtr BR = checkOvershift()) { 102 Ctx.emitReport(std::move(BR)); 103 return; 104 } 105 106 // Report a bug if the right operand is negative: 107 if (BugReportPtr BR = checkOperandNegative(OperandSide::Right)) { 108 Ctx.emitReport(std::move(BR)); 109 return; 110 } 111 112 if (shouldPerformPedanticChecks()) { 113 // Report a bug if the left operand is negative: 114 if (BugReportPtr BR = checkOperandNegative(OperandSide::Left)) { 115 Ctx.emitReport(std::move(BR)); 116 return; 117 } 118 119 // Report a bug when left shift of a concrete signed value overflows: 120 if (BugReportPtr BR = checkLeftShiftOverflow()) { 121 Ctx.emitReport(std::move(BR)); 122 return; 123 } 124 } 125 126 // No bugs detected, update the state and add a single note tag which 127 // summarizes the new assumptions. 128 Ctx.addTransition(FoldedState, createNoteTag()); 129 } 130 131 /// This method checks a requirement that must be satisfied by the value on the 132 /// given Side of a bitwise shift operator in well-defined code. If the 133 /// requirement is incompatible with prior knowledge, this method reports 134 /// failure by returning false. 135 bool BitwiseShiftValidator::assumeRequirement(OperandSide Side, 136 BinaryOperator::Opcode Comparison, 137 unsigned Limit) { 138 SValBuilder &SVB = Ctx.getSValBuilder(); 139 140 const SVal OperandVal = Ctx.getSVal(operandExpr(Side)); 141 const auto LimitVal = SVB.makeIntVal(Limit, Ctx.getASTContext().IntTy); 142 // Note that the type of `LimitVal` must be a signed, because otherwise a 143 // negative `Val` could be converted to a large positive value. 144 145 auto ResultVal = SVB.evalBinOp(FoldedState, Comparison, OperandVal, LimitVal, 146 SVB.getConditionType()); 147 if (auto DURes = ResultVal.getAs<DefinedOrUnknownSVal>()) { 148 auto [StTrue, StFalse] = FoldedState->assume(DURes.value()); 149 if (!StTrue) { 150 // We detected undefined behavior (the caller will report it). 151 FoldedState = StFalse; 152 return false; 153 } 154 // The code may be valid, so let's assume that it's valid: 155 FoldedState = StTrue; 156 if (StFalse) { 157 // Record note tag data for the assumption that we made 158 recordAssumption(Side, Comparison, Limit); 159 } 160 } 161 return true; 162 } 163 164 BugReportPtr BitwiseShiftValidator::checkOvershift() { 165 const QualType LHSTy = Op->getLHS()->getType(); 166 const unsigned LHSBitWidth = Ctx.getASTContext().getIntWidth(LHSTy); 167 168 if (assumeRequirement(OperandSide::Right, BO_LT, LHSBitWidth)) 169 return nullptr; 170 171 const SVal Right = Ctx.getSVal(operandExpr(OperandSide::Right)); 172 173 std::string RightOpStr = "", LowerBoundStr = ""; 174 if (auto ConcreteRight = Right.getAs<nonloc::ConcreteInt>()) 175 RightOpStr = formatv(" '{0}'", ConcreteRight->getValue()); 176 else { 177 SValBuilder &SVB = Ctx.getSValBuilder(); 178 if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right); 179 MinRight && *MinRight >= LHSBitWidth) { 180 LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue()); 181 } 182 } 183 184 std::string ShortMsg = formatv( 185 "{0} shift{1}{2} overflows the capacity of '{3}'", 186 isLeftShift() ? "Left" : "Right", RightOpStr.empty() ? "" : " by", 187 RightOpStr, LHSTy.getAsString()); 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); 193 } 194 195 // Before C++20, at 5.8 [expr.shift] (N4296, 2014-11-19) the standard says 196 // 1. "... The behaviour is undefined if the right operand is negative..." 197 // 2. "The value of E1 << E2 ... 198 // if E1 has a signed type and non-negative value ... 199 // otherwise, the behavior is undefined." 200 // 3. "The value of E1 >> E2 ... 201 // If E1 has a signed type and a negative value, 202 // the resulting value is implementation-defined." 203 // However, negative left arguments work in practice and the C++20 standard 204 // eliminates conditions 2 and 3. 205 BugReportPtr BitwiseShiftValidator::checkOperandNegative(OperandSide Side) { 206 // If the type is unsigned, it cannot be negative 207 if (!operandExpr(Side)->getType()->isSignedIntegerType()) 208 return nullptr; 209 210 // Main check: determine whether the operand is constrained to be negative 211 if (assumeRequirement(Side, BO_GE, 0)) 212 return nullptr; 213 214 std::string ShortMsg = formatv("{0} operand is negative in {1} shift", 215 Side == OperandSide::Left ? "Left" : "Right", 216 shiftDir()) 217 .str(); 218 std::string Msg = formatv("The result of {0} shift is undefined " 219 "because the {1} operand is negative", 220 shiftDir(), 221 Side == OperandSide::Left ? "left" : "right") 222 .str(); 223 224 return createBugReport(ShortMsg, Msg); 225 } 226 227 BugReportPtr BitwiseShiftValidator::checkLeftShiftOverflow() { 228 // A right shift cannot be an overflowing left shift... 229 if (!isLeftShift()) 230 return nullptr; 231 232 // In C++ it's well-defined to shift to the sign bit. In C however, it's UB. 233 // 5.8.2 [expr.shift] (N4296, 2014-11-19) 234 const bool ShouldPreserveSignBit = !Ctx.getLangOpts().CPlusPlus; 235 236 const Expr *LHS = operandExpr(OperandSide::Left); 237 const QualType LHSTy = LHS->getType(); 238 const unsigned LeftBitWidth = Ctx.getASTContext().getIntWidth(LHSTy); 239 assert(LeftBitWidth > 0); 240 241 // Quote "For unsigned lhs, the value of LHS << RHS is the value of LHS * 242 // 2^RHS, reduced modulo maximum value of the return type plus 1." 243 if (LHSTy->isUnsignedIntegerType()) 244 return nullptr; 245 246 // We only support concrete integers as left operand. 247 const auto Left = Ctx.getSVal(LHS).getAs<nonloc::ConcreteInt>(); 248 if (!Left.has_value()) 249 return nullptr; 250 251 // We should have already reported a bug if the left operand of the shift was 252 // negative, so it cannot be negative here. 253 assert(Left->getValue()->isNonNegative()); 254 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; 261 262 if (assumeRequirement(OperandSide::Right, BO_LT, MaximalAllowedShift + 1)) 263 return nullptr; 264 265 const std::string CapacityMsg = 266 formatv("because '{0}' can hold only {1} bits ({2} the sign bit)", 267 LHSTy.getAsString(), LeftAvailableBitWidth, 268 ShouldPreserveSignBit ? "excluding" : "including"); 269 270 const SVal Right = Ctx.getSVal(Op->getRHS()); 271 272 std::string ShortMsg, Msg; 273 if (const auto ConcreteRight = Right.getAs<nonloc::ConcreteInt>()) { 274 // Here ConcreteRight must contain a small non-negative integer, because 275 // otherwise one of the earlier checks should've reported a bug. 276 const int64_t RHS = ConcreteRight->getValue()->getExtValue(); 277 assert(RHS > MaximalAllowedShift); 278 const int64_t OverflownBits = RHS - MaximalAllowedShift; 279 ShortMsg = formatv( 280 "The shift '{0} << {1}' overflows the capacity of '{2}'", 281 Left->getValue(), ConcreteRight->getValue(), LHSTy.getAsString()); 282 Msg = formatv( 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)); 286 } else { 287 ShortMsg = formatv("Left shift of '{0}' overflows the capacity of '{1}'", 288 Left->getValue(), LHSTy.getAsString()); 289 Msg = formatv( 290 "Left shift of '{0}' is undefined {1}, so some bits overflow", 291 Left->getValue(), CapacityMsg); 292 } 293 294 return createBugReport(ShortMsg, Msg); 295 } 296 297 void BitwiseShiftValidator::recordAssumption(OperandSide Side, 298 BinaryOperator::Opcode Comparison, 299 unsigned Limit) { 300 switch (Comparison) { 301 case BO_GE: 302 assert(Limit == 0); 303 NonNegOperands |= (Side == OperandSide::Left ? NonNegLeft : NonNegRight); 304 break; 305 case BO_LT: 306 assert(Side == OperandSide::Right); 307 if (!UpperBoundBitCount || Limit < UpperBoundBitCount.value()) 308 UpperBoundBitCount = Limit; 309 break; 310 default: 311 llvm_unreachable("this checker does not use other comparison operators"); 312 } 313 } 314 315 const NoteTag *BitwiseShiftValidator::createNoteTag() const { 316 if (!NonNegOperands && !UpperBoundBitCount) 317 return nullptr; 318 319 SmallString<128> Buf; 320 llvm::raw_svector_ostream Out(Buf); 321 Out << "Assuming "; 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()); 327 328 return Ctx.getNoteTag(Msg, /*isPrunable=*/true); 329 } 330 331 std::unique_ptr<PathSensitiveBugReport> 332 BitwiseShiftValidator::createBugReport(StringRef ShortMsg, StringRef Msg) const { 333 ProgramStateRef State = Ctx.getState(); 334 if (ExplodedNode *ErrNode = Ctx.generateErrorNode(State)) { 335 auto BR = 336 std::make_unique<PathSensitiveBugReport>(BT, ShortMsg, Msg, ErrNode); 337 bugreporter::trackExpressionValue(ErrNode, Op->getLHS(), *BR); 338 bugreporter::trackExpressionValue(ErrNode, Op->getRHS(), *BR); 339 return BR; 340 } 341 return nullptr; 342 } 343 } // anonymous namespace 344 345 class BitwiseShiftChecker : public Checker<check::PreStmt<BinaryOperator>> { 346 BugType BT{this, "Bitwise shift", "Suspicious operation"}; 347 348 public: 349 void checkPreStmt(const BinaryOperator *B, CheckerContext &Ctx) const { 350 BinaryOperator::Opcode Op = B->getOpcode(); 351 352 if (Op != BO_Shl && Op != BO_Shr) 353 return; 354 355 BitwiseShiftValidator(B, Ctx, BT, Pedantic).run(); 356 } 357 358 bool Pedantic = false; 359 }; 360 361 void ento::registerBitwiseShiftChecker(CheckerManager &Mgr) { 362 auto *Chk = Mgr.registerChecker<BitwiseShiftChecker>(); 363 const AnalyzerOptions &Opts = Mgr.getAnalyzerOptions(); 364 Chk->Pedantic = Opts.getCheckerBooleanOption(Chk, "Pedantic"); 365 } 366 367 bool ento::shouldRegisterBitwiseShiftChecker(const CheckerManager &mgr) { 368 return true; 369 } 370