xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- UndefinedAssignmentChecker.h ---------------------------*- 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 UndefinedAssignmentChecker, a builtin check in ExprEngine that
107330f729Sjoerg // checks for assigning undefined values.
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"
197330f729Sjoerg 
207330f729Sjoerg using namespace clang;
217330f729Sjoerg using namespace ento;
227330f729Sjoerg 
237330f729Sjoerg namespace {
247330f729Sjoerg class UndefinedAssignmentChecker
257330f729Sjoerg   : public Checker<check::Bind> {
267330f729Sjoerg   mutable std::unique_ptr<BugType> BT;
277330f729Sjoerg 
287330f729Sjoerg public:
297330f729Sjoerg   void checkBind(SVal location, SVal val, const Stmt *S,
307330f729Sjoerg                  CheckerContext &C) const;
317330f729Sjoerg };
327330f729Sjoerg }
337330f729Sjoerg 
checkBind(SVal location,SVal val,const Stmt * StoreE,CheckerContext & C) const347330f729Sjoerg void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
357330f729Sjoerg                                            const Stmt *StoreE,
367330f729Sjoerg                                            CheckerContext &C) const {
377330f729Sjoerg   if (!val.isUndef())
387330f729Sjoerg     return;
397330f729Sjoerg 
407330f729Sjoerg   // Do not report assignments of uninitialized values inside swap functions.
417330f729Sjoerg   // This should allow to swap partially uninitialized structs
427330f729Sjoerg   // (radar://14129997)
437330f729Sjoerg   if (const FunctionDecl *EnclosingFunctionDecl =
447330f729Sjoerg       dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
457330f729Sjoerg     if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
467330f729Sjoerg       return;
477330f729Sjoerg 
487330f729Sjoerg   ExplodedNode *N = C.generateErrorNode();
497330f729Sjoerg 
507330f729Sjoerg   if (!N)
517330f729Sjoerg     return;
527330f729Sjoerg 
537330f729Sjoerg   static const char *const DefaultMsg =
547330f729Sjoerg       "Assigned value is garbage or undefined";
557330f729Sjoerg   if (!BT)
567330f729Sjoerg     BT.reset(new BuiltinBug(this, DefaultMsg));
577330f729Sjoerg 
587330f729Sjoerg   // Generate a report for this bug.
597330f729Sjoerg   llvm::SmallString<128> Str;
607330f729Sjoerg   llvm::raw_svector_ostream OS(Str);
617330f729Sjoerg 
627330f729Sjoerg   const Expr *ex = nullptr;
637330f729Sjoerg 
647330f729Sjoerg   while (StoreE) {
657330f729Sjoerg     if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
667330f729Sjoerg       OS << "The expression is an uninitialized value. "
677330f729Sjoerg             "The computed value will also be garbage";
687330f729Sjoerg 
697330f729Sjoerg       ex = U->getSubExpr();
707330f729Sjoerg       break;
717330f729Sjoerg     }
727330f729Sjoerg 
737330f729Sjoerg     if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
747330f729Sjoerg       if (B->isCompoundAssignmentOp()) {
757330f729Sjoerg         if (C.getSVal(B->getLHS()).isUndef()) {
767330f729Sjoerg           OS << "The left expression of the compound assignment is an "
777330f729Sjoerg                 "uninitialized value. The computed value will also be garbage";
787330f729Sjoerg           ex = B->getLHS();
797330f729Sjoerg           break;
807330f729Sjoerg         }
817330f729Sjoerg       }
827330f729Sjoerg 
837330f729Sjoerg       ex = B->getRHS();
847330f729Sjoerg       break;
857330f729Sjoerg     }
867330f729Sjoerg 
877330f729Sjoerg     if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
887330f729Sjoerg       const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
897330f729Sjoerg       ex = VD->getInit();
907330f729Sjoerg     }
917330f729Sjoerg 
927330f729Sjoerg     if (const auto *CD =
937330f729Sjoerg             dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
947330f729Sjoerg       if (CD->isImplicit()) {
957330f729Sjoerg         for (auto I : CD->inits()) {
967330f729Sjoerg           if (I->getInit()->IgnoreImpCasts() == StoreE) {
977330f729Sjoerg             OS << "Value assigned to field '" << I->getMember()->getName()
987330f729Sjoerg                << "' in implicit constructor is garbage or undefined";
997330f729Sjoerg             break;
1007330f729Sjoerg           }
1017330f729Sjoerg         }
1027330f729Sjoerg       }
1037330f729Sjoerg     }
1047330f729Sjoerg 
1057330f729Sjoerg     break;
1067330f729Sjoerg   }
1077330f729Sjoerg 
1087330f729Sjoerg   if (OS.str().empty())
1097330f729Sjoerg     OS << DefaultMsg;
1107330f729Sjoerg 
1117330f729Sjoerg   auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
1127330f729Sjoerg   if (ex) {
1137330f729Sjoerg     R->addRange(ex->getSourceRange());
1147330f729Sjoerg     bugreporter::trackExpressionValue(N, ex, *R);
1157330f729Sjoerg   }
1167330f729Sjoerg   C.emitReport(std::move(R));
1177330f729Sjoerg }
1187330f729Sjoerg 
registerUndefinedAssignmentChecker(CheckerManager & mgr)1197330f729Sjoerg void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
1207330f729Sjoerg   mgr.registerChecker<UndefinedAssignmentChecker>();
1217330f729Sjoerg }
1227330f729Sjoerg 
shouldRegisterUndefinedAssignmentChecker(const CheckerManager & mgr)123*e038c9c4Sjoerg bool ento::shouldRegisterUndefinedAssignmentChecker(const CheckerManager &mgr) {
1247330f729Sjoerg   return true;
1257330f729Sjoerg }
126