12754fe60SDimitry Andric //===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
22754fe60SDimitry Andric //
32754fe60SDimitry Andric // The LLVM Compiler Infrastructure
42754fe60SDimitry Andric //
52754fe60SDimitry Andric // This file is distributed under the University of Illinois Open Source
62754fe60SDimitry Andric // License. See LICENSE.TXT for details.
72754fe60SDimitry Andric //
82754fe60SDimitry Andric //===----------------------------------------------------------------------===//
92754fe60SDimitry Andric //
103b0f4066SDimitry Andric // This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that
112754fe60SDimitry Andric // checks for assigning undefined values.
122754fe60SDimitry Andric //
132754fe60SDimitry Andric //===----------------------------------------------------------------------===//
142754fe60SDimitry Andric
15*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16139f7f9bSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
173b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
183b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
193b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
202754fe60SDimitry Andric
212754fe60SDimitry Andric using namespace clang;
222754fe60SDimitry Andric using namespace ento;
232754fe60SDimitry Andric
242754fe60SDimitry Andric namespace {
252754fe60SDimitry Andric class UndefinedAssignmentChecker
263b0f4066SDimitry Andric : public Checker<check::Bind> {
2759d1ed5bSDimitry Andric mutable std::unique_ptr<BugType> BT;
283b0f4066SDimitry Andric
292754fe60SDimitry Andric public:
306122f3e6SDimitry Andric void checkBind(SVal location, SVal val, const Stmt *S,
316122f3e6SDimitry Andric CheckerContext &C) const;
322754fe60SDimitry Andric };
332754fe60SDimitry Andric }
342754fe60SDimitry Andric
checkBind(SVal location,SVal val,const Stmt * StoreE,CheckerContext & C) const353b0f4066SDimitry Andric void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
366122f3e6SDimitry Andric const Stmt *StoreE,
373b0f4066SDimitry Andric CheckerContext &C) const {
382754fe60SDimitry Andric if (!val.isUndef())
392754fe60SDimitry Andric return;
402754fe60SDimitry Andric
41f785676fSDimitry Andric // Do not report assignments of uninitialized values inside swap functions.
42f785676fSDimitry Andric // This should allow to swap partially uninitialized structs
43f785676fSDimitry Andric // (radar://14129997)
44f785676fSDimitry Andric if (const FunctionDecl *EnclosingFunctionDecl =
45f785676fSDimitry Andric dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
46f785676fSDimitry Andric if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
47f785676fSDimitry Andric return;
48f785676fSDimitry Andric
490623d748SDimitry Andric ExplodedNode *N = C.generateErrorNode();
502754fe60SDimitry Andric
512754fe60SDimitry Andric if (!N)
522754fe60SDimitry Andric return;
532754fe60SDimitry Andric
544ba319b5SDimitry Andric static const char *const DefaultMsg =
554ba319b5SDimitry Andric "Assigned value is garbage or undefined";
562754fe60SDimitry Andric if (!BT)
574ba319b5SDimitry Andric BT.reset(new BuiltinBug(this, DefaultMsg));
582754fe60SDimitry Andric
592754fe60SDimitry Andric // Generate a report for this bug.
604ba319b5SDimitry Andric llvm::SmallString<128> Str;
614ba319b5SDimitry Andric llvm::raw_svector_ostream OS(Str);
624ba319b5SDimitry Andric
6359d1ed5bSDimitry Andric const Expr *ex = nullptr;
642754fe60SDimitry Andric
652754fe60SDimitry Andric while (StoreE) {
669a199699SDimitry Andric if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
674ba319b5SDimitry Andric OS << "The expression is an uninitialized value. "
689a199699SDimitry Andric "The computed value will also be garbage";
699a199699SDimitry Andric
709a199699SDimitry Andric ex = U->getSubExpr();
719a199699SDimitry Andric break;
729a199699SDimitry Andric }
739a199699SDimitry Andric
742754fe60SDimitry Andric if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
752754fe60SDimitry Andric if (B->isCompoundAssignmentOp()) {
764ba319b5SDimitry Andric if (C.getSVal(B->getLHS()).isUndef()) {
774ba319b5SDimitry Andric OS << "The left expression of the compound assignment is an "
782754fe60SDimitry Andric "uninitialized value. The computed value will also be garbage";
792754fe60SDimitry Andric ex = B->getLHS();
802754fe60SDimitry Andric break;
812754fe60SDimitry Andric }
822754fe60SDimitry Andric }
832754fe60SDimitry Andric
842754fe60SDimitry Andric ex = B->getRHS();
852754fe60SDimitry Andric break;
862754fe60SDimitry Andric }
872754fe60SDimitry Andric
882754fe60SDimitry Andric if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
892754fe60SDimitry Andric const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
902754fe60SDimitry Andric ex = VD->getInit();
912754fe60SDimitry Andric }
922754fe60SDimitry Andric
934ba319b5SDimitry Andric if (const auto *CD =
944ba319b5SDimitry Andric dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
954ba319b5SDimitry Andric if (CD->isImplicit()) {
964ba319b5SDimitry Andric for (auto I : CD->inits()) {
974ba319b5SDimitry Andric if (I->getInit()->IgnoreImpCasts() == StoreE) {
984ba319b5SDimitry Andric OS << "Value assigned to field '" << I->getMember()->getName()
994ba319b5SDimitry Andric << "' in implicit constructor is garbage or undefined";
1004ba319b5SDimitry Andric break;
1014ba319b5SDimitry Andric }
1024ba319b5SDimitry Andric }
1034ba319b5SDimitry Andric }
1044ba319b5SDimitry Andric }
1054ba319b5SDimitry Andric
1062754fe60SDimitry Andric break;
1072754fe60SDimitry Andric }
1082754fe60SDimitry Andric
1094ba319b5SDimitry Andric if (OS.str().empty())
1104ba319b5SDimitry Andric OS << DefaultMsg;
1114ba319b5SDimitry Andric
1124ba319b5SDimitry Andric auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
1132754fe60SDimitry Andric if (ex) {
1142754fe60SDimitry Andric R->addRange(ex->getSourceRange());
115*b5893f02SDimitry Andric bugreporter::trackExpressionValue(N, ex, *R);
1162754fe60SDimitry Andric }
1173dac3a9bSDimitry Andric C.emitReport(std::move(R));
1182754fe60SDimitry Andric }
1192754fe60SDimitry Andric
registerUndefinedAssignmentChecker(CheckerManager & mgr)1203b0f4066SDimitry Andric void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
1213b0f4066SDimitry Andric mgr.registerChecker<UndefinedAssignmentChecker>();
1223b0f4066SDimitry Andric }
123