xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This defines UndefResultChecker, a builtin check in ExprEngine that
107330f729Sjoerg // performs checks for undefined results of non-assignment binary operators.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
157330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
167330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
207330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
217330f729Sjoerg #include "llvm/ADT/SmallString.h"
227330f729Sjoerg #include "llvm/Support/raw_ostream.h"
237330f729Sjoerg 
247330f729Sjoerg using namespace clang;
257330f729Sjoerg using namespace ento;
267330f729Sjoerg 
277330f729Sjoerg namespace {
287330f729Sjoerg class UndefResultChecker
297330f729Sjoerg   : public Checker< check::PostStmt<BinaryOperator> > {
307330f729Sjoerg 
317330f729Sjoerg   mutable std::unique_ptr<BugType> BT;
327330f729Sjoerg 
337330f729Sjoerg public:
347330f729Sjoerg   void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
357330f729Sjoerg };
367330f729Sjoerg } // end anonymous namespace
377330f729Sjoerg 
isArrayIndexOutOfBounds(CheckerContext & C,const Expr * Ex)387330f729Sjoerg static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
397330f729Sjoerg   ProgramStateRef state = C.getState();
407330f729Sjoerg 
417330f729Sjoerg   if (!isa<ArraySubscriptExpr>(Ex))
427330f729Sjoerg     return false;
437330f729Sjoerg 
447330f729Sjoerg   SVal Loc = C.getSVal(Ex);
457330f729Sjoerg   if (!Loc.isValid())
467330f729Sjoerg     return false;
477330f729Sjoerg 
487330f729Sjoerg   const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion();
497330f729Sjoerg   const ElementRegion *ER = dyn_cast<ElementRegion>(MR);
507330f729Sjoerg   if (!ER)
517330f729Sjoerg     return false;
527330f729Sjoerg 
537330f729Sjoerg   DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
54*e038c9c4Sjoerg   DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
55*e038c9c4Sjoerg       state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
56*e038c9c4Sjoerg   ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
57*e038c9c4Sjoerg   ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
587330f729Sjoerg   return StOutBound && !StInBound;
597330f729Sjoerg }
607330f729Sjoerg 
isShiftOverflow(const BinaryOperator * B,CheckerContext & C)617330f729Sjoerg static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
627330f729Sjoerg   return C.isGreaterOrEqual(
637330f729Sjoerg       B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
647330f729Sjoerg }
657330f729Sjoerg 
isLeftShiftResultUnrepresentable(const BinaryOperator * B,CheckerContext & C)667330f729Sjoerg static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
677330f729Sjoerg                                              CheckerContext &C) {
687330f729Sjoerg   SValBuilder &SB = C.getSValBuilder();
697330f729Sjoerg   ProgramStateRef State = C.getState();
707330f729Sjoerg   const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
717330f729Sjoerg   const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
727330f729Sjoerg   assert(LHS && RHS && "Values unknown, inconsistent state");
737330f729Sjoerg   return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
747330f729Sjoerg }
757330f729Sjoerg 
checkPostStmt(const BinaryOperator * B,CheckerContext & C) const767330f729Sjoerg void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
777330f729Sjoerg                                        CheckerContext &C) const {
787330f729Sjoerg   if (C.getSVal(B).isUndef()) {
797330f729Sjoerg 
807330f729Sjoerg     // Do not report assignments of uninitialized values inside swap functions.
817330f729Sjoerg     // This should allow to swap partially uninitialized structs
827330f729Sjoerg     // (radar://14129997)
837330f729Sjoerg     if (const FunctionDecl *EnclosingFunctionDecl =
847330f729Sjoerg         dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
857330f729Sjoerg       if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
867330f729Sjoerg         return;
877330f729Sjoerg 
887330f729Sjoerg     // Generate an error node.
897330f729Sjoerg     ExplodedNode *N = C.generateErrorNode();
907330f729Sjoerg     if (!N)
917330f729Sjoerg       return;
927330f729Sjoerg 
937330f729Sjoerg     if (!BT)
947330f729Sjoerg       BT.reset(
957330f729Sjoerg           new BuiltinBug(this, "Result of operation is garbage or undefined"));
967330f729Sjoerg 
977330f729Sjoerg     SmallString<256> sbuf;
987330f729Sjoerg     llvm::raw_svector_ostream OS(sbuf);
997330f729Sjoerg     const Expr *Ex = nullptr;
1007330f729Sjoerg     bool isLeft = true;
1017330f729Sjoerg 
1027330f729Sjoerg     if (C.getSVal(B->getLHS()).isUndef()) {
1037330f729Sjoerg       Ex = B->getLHS()->IgnoreParenCasts();
1047330f729Sjoerg       isLeft = true;
1057330f729Sjoerg     }
1067330f729Sjoerg     else if (C.getSVal(B->getRHS()).isUndef()) {
1077330f729Sjoerg       Ex = B->getRHS()->IgnoreParenCasts();
1087330f729Sjoerg       isLeft = false;
1097330f729Sjoerg     }
1107330f729Sjoerg 
1117330f729Sjoerg     if (Ex) {
1127330f729Sjoerg       OS << "The " << (isLeft ? "left" : "right") << " operand of '"
1137330f729Sjoerg          << BinaryOperator::getOpcodeStr(B->getOpcode())
1147330f729Sjoerg          << "' is a garbage value";
1157330f729Sjoerg       if (isArrayIndexOutOfBounds(C, Ex))
1167330f729Sjoerg         OS << " due to array index out of bounds";
1177330f729Sjoerg     } else {
1187330f729Sjoerg       // Neither operand was undefined, but the result is undefined.
1197330f729Sjoerg       if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
1207330f729Sjoerg            B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
1217330f729Sjoerg           C.isNegative(B->getRHS())) {
1227330f729Sjoerg         OS << "The result of the "
1237330f729Sjoerg            << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
1247330f729Sjoerg                                                               : "right")
1257330f729Sjoerg            << " shift is undefined because the right operand is negative";
1267330f729Sjoerg         Ex = B->getRHS();
1277330f729Sjoerg       } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
1287330f729Sjoerg                   B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
1297330f729Sjoerg                  isShiftOverflow(B, C)) {
1307330f729Sjoerg 
1317330f729Sjoerg         OS << "The result of the "
1327330f729Sjoerg            << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
1337330f729Sjoerg                                                               : "right")
1347330f729Sjoerg            << " shift is undefined due to shifting by ";
1357330f729Sjoerg         Ex = B->getRHS();
1367330f729Sjoerg 
1377330f729Sjoerg         SValBuilder &SB = C.getSValBuilder();
1387330f729Sjoerg         const llvm::APSInt *I =
1397330f729Sjoerg             SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
1407330f729Sjoerg         if (!I)
1417330f729Sjoerg           OS << "a value that is";
1427330f729Sjoerg         else if (I->isUnsigned())
1437330f729Sjoerg           OS << '\'' << I->getZExtValue() << "\', which is";
1447330f729Sjoerg         else
1457330f729Sjoerg           OS << '\'' << I->getSExtValue() << "\', which is";
1467330f729Sjoerg 
1477330f729Sjoerg         OS << " greater or equal to the width of type '"
1487330f729Sjoerg            << B->getLHS()->getType().getAsString() << "'.";
1497330f729Sjoerg       } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
1507330f729Sjoerg                  C.isNegative(B->getLHS())) {
1517330f729Sjoerg         OS << "The result of the left shift is undefined because the left "
1527330f729Sjoerg               "operand is negative";
1537330f729Sjoerg         Ex = B->getLHS();
1547330f729Sjoerg       } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
1557330f729Sjoerg                  isLeftShiftResultUnrepresentable(B, C)) {
1567330f729Sjoerg         ProgramStateRef State = C.getState();
1577330f729Sjoerg         SValBuilder &SB = C.getSValBuilder();
1587330f729Sjoerg         const llvm::APSInt *LHS =
1597330f729Sjoerg             SB.getKnownValue(State, C.getSVal(B->getLHS()));
1607330f729Sjoerg         const llvm::APSInt *RHS =
1617330f729Sjoerg             SB.getKnownValue(State, C.getSVal(B->getRHS()));
1627330f729Sjoerg         OS << "The result of the left shift is undefined due to shifting \'"
1637330f729Sjoerg            << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
1647330f729Sjoerg            << "\', which is unrepresentable in the unsigned version of "
1657330f729Sjoerg            << "the return type \'" << B->getLHS()->getType().getAsString()
1667330f729Sjoerg            << "\'";
1677330f729Sjoerg         Ex = B->getLHS();
1687330f729Sjoerg       } else {
1697330f729Sjoerg         OS << "The result of the '"
1707330f729Sjoerg            << BinaryOperator::getOpcodeStr(B->getOpcode())
1717330f729Sjoerg            << "' expression is undefined";
1727330f729Sjoerg       }
1737330f729Sjoerg     }
1747330f729Sjoerg     auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
1757330f729Sjoerg     if (Ex) {
1767330f729Sjoerg       report->addRange(Ex->getSourceRange());
1777330f729Sjoerg       bugreporter::trackExpressionValue(N, Ex, *report);
1787330f729Sjoerg     }
1797330f729Sjoerg     else
1807330f729Sjoerg       bugreporter::trackExpressionValue(N, B, *report);
1817330f729Sjoerg 
1827330f729Sjoerg     C.emitReport(std::move(report));
1837330f729Sjoerg   }
1847330f729Sjoerg }
1857330f729Sjoerg 
registerUndefResultChecker(CheckerManager & mgr)1867330f729Sjoerg void ento::registerUndefResultChecker(CheckerManager &mgr) {
1877330f729Sjoerg   mgr.registerChecker<UndefResultChecker>();
1887330f729Sjoerg }
1897330f729Sjoerg 
shouldRegisterUndefResultChecker(const CheckerManager & mgr)190*e038c9c4Sjoerg bool ento::shouldRegisterUndefResultChecker(const CheckerManager &mgr) {
1917330f729Sjoerg   return true;
1927330f729Sjoerg }
193