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