1e5dd7070Spatrick //===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// 2e5dd7070Spatrick // 3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e5dd7070Spatrick // 7e5dd7070Spatrick //===----------------------------------------------------------------------===// 8e5dd7070Spatrick // 9e5dd7070Spatrick // This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that 10e5dd7070Spatrick // checks for assigning undefined values. 11e5dd7070Spatrick // 12e5dd7070Spatrick //===----------------------------------------------------------------------===// 13e5dd7070Spatrick 14e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h" 17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19e5dd7070Spatrick 20e5dd7070Spatrick using namespace clang; 21e5dd7070Spatrick using namespace ento; 22e5dd7070Spatrick 23e5dd7070Spatrick namespace { 24e5dd7070Spatrick class UndefinedAssignmentChecker 25e5dd7070Spatrick : public Checker<check::Bind> { 26e5dd7070Spatrick mutable std::unique_ptr<BugType> BT; 27e5dd7070Spatrick 28e5dd7070Spatrick public: 29e5dd7070Spatrick void checkBind(SVal location, SVal val, const Stmt *S, 30e5dd7070Spatrick CheckerContext &C) const; 31e5dd7070Spatrick }; 32e5dd7070Spatrick } 33e5dd7070Spatrick 34e5dd7070Spatrick void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, 35e5dd7070Spatrick const Stmt *StoreE, 36e5dd7070Spatrick CheckerContext &C) const { 37e5dd7070Spatrick if (!val.isUndef()) 38e5dd7070Spatrick return; 39e5dd7070Spatrick 40e5dd7070Spatrick // Do not report assignments of uninitialized values inside swap functions. 41e5dd7070Spatrick // This should allow to swap partially uninitialized structs 42e5dd7070Spatrick // (radar://14129997) 43e5dd7070Spatrick if (const FunctionDecl *EnclosingFunctionDecl = 44e5dd7070Spatrick dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) 45e5dd7070Spatrick if (C.getCalleeName(EnclosingFunctionDecl) == "swap") 46e5dd7070Spatrick return; 47e5dd7070Spatrick 48e5dd7070Spatrick ExplodedNode *N = C.generateErrorNode(); 49e5dd7070Spatrick 50e5dd7070Spatrick if (!N) 51e5dd7070Spatrick return; 52e5dd7070Spatrick 53e5dd7070Spatrick static const char *const DefaultMsg = 54e5dd7070Spatrick "Assigned value is garbage or undefined"; 55e5dd7070Spatrick if (!BT) 56e5dd7070Spatrick BT.reset(new BuiltinBug(this, DefaultMsg)); 57e5dd7070Spatrick 58e5dd7070Spatrick // Generate a report for this bug. 59e5dd7070Spatrick llvm::SmallString<128> Str; 60e5dd7070Spatrick llvm::raw_svector_ostream OS(Str); 61e5dd7070Spatrick 62e5dd7070Spatrick const Expr *ex = nullptr; 63e5dd7070Spatrick 64e5dd7070Spatrick while (StoreE) { 65e5dd7070Spatrick if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) { 66e5dd7070Spatrick OS << "The expression is an uninitialized value. " 67e5dd7070Spatrick "The computed value will also be garbage"; 68e5dd7070Spatrick 69e5dd7070Spatrick ex = U->getSubExpr(); 70e5dd7070Spatrick break; 71e5dd7070Spatrick } 72e5dd7070Spatrick 73e5dd7070Spatrick if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { 74e5dd7070Spatrick if (B->isCompoundAssignmentOp()) { 75e5dd7070Spatrick if (C.getSVal(B->getLHS()).isUndef()) { 76e5dd7070Spatrick OS << "The left expression of the compound assignment is an " 77e5dd7070Spatrick "uninitialized value. The computed value will also be garbage"; 78e5dd7070Spatrick ex = B->getLHS(); 79e5dd7070Spatrick break; 80e5dd7070Spatrick } 81e5dd7070Spatrick } 82e5dd7070Spatrick 83e5dd7070Spatrick ex = B->getRHS(); 84e5dd7070Spatrick break; 85e5dd7070Spatrick } 86e5dd7070Spatrick 87e5dd7070Spatrick if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { 88e5dd7070Spatrick const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); 89e5dd7070Spatrick ex = VD->getInit(); 90e5dd7070Spatrick } 91e5dd7070Spatrick 92e5dd7070Spatrick if (const auto *CD = 93e5dd7070Spatrick dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) { 94e5dd7070Spatrick if (CD->isImplicit()) { 95e5dd7070Spatrick for (auto I : CD->inits()) { 96e5dd7070Spatrick if (I->getInit()->IgnoreImpCasts() == StoreE) { 97e5dd7070Spatrick OS << "Value assigned to field '" << I->getMember()->getName() 98e5dd7070Spatrick << "' in implicit constructor is garbage or undefined"; 99e5dd7070Spatrick break; 100e5dd7070Spatrick } 101e5dd7070Spatrick } 102e5dd7070Spatrick } 103e5dd7070Spatrick } 104e5dd7070Spatrick 105e5dd7070Spatrick break; 106e5dd7070Spatrick } 107e5dd7070Spatrick 108e5dd7070Spatrick if (OS.str().empty()) 109e5dd7070Spatrick OS << DefaultMsg; 110e5dd7070Spatrick 111e5dd7070Spatrick auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); 112e5dd7070Spatrick if (ex) { 113e5dd7070Spatrick R->addRange(ex->getSourceRange()); 114e5dd7070Spatrick bugreporter::trackExpressionValue(N, ex, *R); 115e5dd7070Spatrick } 116e5dd7070Spatrick C.emitReport(std::move(R)); 117e5dd7070Spatrick } 118e5dd7070Spatrick 119e5dd7070Spatrick void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) { 120e5dd7070Spatrick mgr.registerChecker<UndefinedAssignmentChecker>(); 121e5dd7070Spatrick } 122e5dd7070Spatrick 123*ec727ea7Spatrick bool ento::shouldRegisterUndefinedAssignmentChecker(const CheckerManager &mgr) { 124e5dd7070Spatrick return true; 125e5dd7070Spatrick } 126