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