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