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