xref: /src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp (revision 647cbc5de815c5651677bf8582797f716ec7b48d)
151fb8b01SRoman Divacky //===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
251fb8b01SRoman Divacky //
322989816SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
422989816SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
522989816SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
651fb8b01SRoman Divacky //
751fb8b01SRoman Divacky //===----------------------------------------------------------------------===//
851fb8b01SRoman Divacky //
901af97d3SDimitry Andric // This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that
1051fb8b01SRoman Divacky // checks for assigning undefined values.
1151fb8b01SRoman Divacky //
1251fb8b01SRoman Divacky //===----------------------------------------------------------------------===//
1351fb8b01SRoman Divacky 
14676fbe81SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15809500fcSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1601af97d3SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
1701af97d3SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
1801af97d3SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
1951fb8b01SRoman Divacky 
2051fb8b01SRoman Divacky using namespace clang;
21bca07a45SDimitry Andric using namespace ento;
2251fb8b01SRoman Divacky 
231569ce68SRoman Divacky namespace {
241569ce68SRoman Divacky class UndefinedAssignmentChecker
2501af97d3SDimitry Andric   : public Checker<check::Bind> {
2677dbea07SDimitry Andric   const BugType BT{this, "Assigned value is garbage or undefined"};
2701af97d3SDimitry Andric 
281569ce68SRoman Divacky public:
2936981b17SDimitry Andric   void checkBind(SVal location, SVal val, const Stmt *S,
3036981b17SDimitry Andric                  CheckerContext &C) const;
311569ce68SRoman Divacky };
321569ce68SRoman Divacky }
331569ce68SRoman Divacky 
checkBind(SVal location,SVal val,const Stmt * StoreE,CheckerContext & C) const3401af97d3SDimitry Andric void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
3536981b17SDimitry Andric                                            const Stmt *StoreE,
3601af97d3SDimitry Andric                                            CheckerContext &C) const {
3751fb8b01SRoman Divacky   if (!val.isUndef())
3851fb8b01SRoman Divacky     return;
3951fb8b01SRoman Divacky 
40bfef3995SDimitry Andric   // Do not report assignments of uninitialized values inside swap functions.
41bfef3995SDimitry Andric   // This should allow to swap partially uninitialized structs
42bfef3995SDimitry Andric   if (const FunctionDecl *EnclosingFunctionDecl =
43bfef3995SDimitry Andric       dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
44bfef3995SDimitry Andric     if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
45bfef3995SDimitry Andric       return;
46bfef3995SDimitry Andric 
4745b53394SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
4851fb8b01SRoman Divacky 
4951fb8b01SRoman Divacky   if (!N)
5051fb8b01SRoman Divacky     return;
5151fb8b01SRoman Divacky 
5251fb8b01SRoman Divacky   // Generate a report for this bug.
5348675466SDimitry Andric   llvm::SmallString<128> Str;
5448675466SDimitry Andric   llvm::raw_svector_ostream OS(Str);
5548675466SDimitry Andric 
569f4dbff6SDimitry Andric   const Expr *ex = nullptr;
5751fb8b01SRoman Divacky 
583d1dcd9bSDimitry Andric   while (StoreE) {
59461a67faSDimitry Andric     if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
6048675466SDimitry Andric       OS << "The expression is an uninitialized value. "
61461a67faSDimitry Andric             "The computed value will also be garbage";
62461a67faSDimitry Andric 
63461a67faSDimitry Andric       ex = U->getSubExpr();
64461a67faSDimitry Andric       break;
65461a67faSDimitry Andric     }
66461a67faSDimitry Andric 
673d1dcd9bSDimitry Andric     if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
6811d2b2d2SRoman Divacky       if (B->isCompoundAssignmentOp()) {
6948675466SDimitry Andric         if (C.getSVal(B->getLHS()).isUndef()) {
7048675466SDimitry Andric           OS << "The left expression of the compound assignment is an "
7111d2b2d2SRoman Divacky                 "uninitialized value. The computed value will also be garbage";
7211d2b2d2SRoman Divacky           ex = B->getLHS();
7311d2b2d2SRoman Divacky           break;
7411d2b2d2SRoman Divacky         }
7511d2b2d2SRoman Divacky       }
7611d2b2d2SRoman Divacky 
7751fb8b01SRoman Divacky       ex = B->getRHS();
7811d2b2d2SRoman Divacky       break;
7911d2b2d2SRoman Divacky     }
8011d2b2d2SRoman Divacky 
813d1dcd9bSDimitry Andric     if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
82519fc96cSDimitry Andric       const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
8351fb8b01SRoman Divacky       ex = VD->getInit();
8451fb8b01SRoman Divacky     }
8511d2b2d2SRoman Divacky 
8648675466SDimitry Andric     if (const auto *CD =
8748675466SDimitry Andric             dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
8848675466SDimitry Andric       if (CD->isImplicit()) {
89e3b55780SDimitry Andric         for (auto *I : CD->inits()) {
9048675466SDimitry Andric           if (I->getInit()->IgnoreImpCasts() == StoreE) {
9148675466SDimitry Andric             OS << "Value assigned to field '" << I->getMember()->getName()
9248675466SDimitry Andric                << "' in implicit constructor is garbage or undefined";
9348675466SDimitry Andric             break;
9448675466SDimitry Andric           }
9548675466SDimitry Andric         }
9648675466SDimitry Andric       }
9748675466SDimitry Andric     }
9848675466SDimitry Andric 
9911d2b2d2SRoman Divacky     break;
10011d2b2d2SRoman Divacky   }
10111d2b2d2SRoman Divacky 
10248675466SDimitry Andric   if (OS.str().empty())
10377dbea07SDimitry Andric     OS << BT.getDescription();
10448675466SDimitry Andric 
10577dbea07SDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
10651fb8b01SRoman Divacky   if (ex) {
10751fb8b01SRoman Divacky     R->addRange(ex->getSourceRange());
108676fbe81SDimitry Andric     bugreporter::trackExpressionValue(N, ex, *R);
10951fb8b01SRoman Divacky   }
110c192b3dcSDimitry Andric   C.emitReport(std::move(R));
11151fb8b01SRoman Divacky }
11251fb8b01SRoman Divacky 
registerUndefinedAssignmentChecker(CheckerManager & mgr)11301af97d3SDimitry Andric void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
11401af97d3SDimitry Andric   mgr.registerChecker<UndefinedAssignmentChecker>();
11501af97d3SDimitry Andric }
11622989816SDimitry Andric 
shouldRegisterUndefinedAssignmentChecker(const CheckerManager & mgr)117cfca06d7SDimitry Andric bool ento::shouldRegisterUndefinedAssignmentChecker(const CheckerManager &mgr) {
11822989816SDimitry Andric   return true;
11922989816SDimitry Andric }
120