xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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 
checkBind(SVal location,SVal val,const Stmt * StoreE,CheckerContext & C) const34e5dd7070Spatrick 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()) {
95*12c85518Srobert         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 
registerUndefinedAssignmentChecker(CheckerManager & mgr)119e5dd7070Spatrick void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
120e5dd7070Spatrick   mgr.registerChecker<UndefinedAssignmentChecker>();
121e5dd7070Spatrick }
122e5dd7070Spatrick 
shouldRegisterUndefinedAssignmentChecker(const CheckerManager & mgr)123ec727ea7Spatrick bool ento::shouldRegisterUndefinedAssignmentChecker(const CheckerManager &mgr) {
124e5dd7070Spatrick   return true;
125e5dd7070Spatrick }
126