diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-05-05 12:59:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-05-05 12:59:04 +0000 |
commit | d7c84d5ce43a2c2584ef92e54d7301455413aff2 (patch) | |
tree | cbe525e7eecd68b76e901cc279c8a4de5e56b020 /contrib/llvm/tools/clang/lib/StaticAnalyzer | |
parent | 6a5634d3ac24c64a5c1e444e00911b80b01b6dc8 (diff) | |
download | src-test2-d7c84d5ce43a2c2584ef92e54d7301455413aff2.tar.gz src-test2-d7c84d5ce43a2c2584ef92e54d7301455413aff2.zip |
Notes
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer')
172 files changed, 6770 insertions, 5462 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h index c8193f77f928..25de37003319 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h @@ -1,9 +1,8 @@ //===--- AllocationState.h ------------------------------------- *- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp index b5d0f6620a1d..d0def6918932 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -1,9 +1,8 @@ //===- AnalysisOrderChecker - Print callbacks called ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -45,8 +44,8 @@ class AnalysisOrderChecker check::LiveSymbols> { bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const { - return Opts.getCheckerBooleanOption("*", false, this) || - Opts.getCheckerBooleanOption(CallbackName, false, this); + return Opts.getCheckerBooleanOption(this, "*") || + Opts.getCheckerBooleanOption(this, CallbackName); } bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const { @@ -176,3 +175,7 @@ public: void ento::registerAnalysisOrderChecker(CheckerManager &mgr) { mgr.registerChecker<AnalysisOrderChecker>(); } + +bool ento::shouldRegisterAnalysisOrderChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index 5e01012401b2..20f3008b4a4b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -1,9 +1,8 @@ //==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file reports various statistics about analyzer visitation. @@ -140,3 +139,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) { mgr.registerChecker<AnalyzerStatsChecker>(); } + +bool ento::shouldRegisterAnalyzerStatsChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 20f3092fdba4..58017acb4a24 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -1,9 +1,8 @@ //== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -91,3 +90,7 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, void ento::registerArrayBoundChecker(CheckerManager &mgr) { mgr.registerChecker<ArrayBoundChecker>(); } + +bool ento::shouldRegisterArrayBoundChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 26887be9f258..3bf8a1836b19 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -1,9 +1,8 @@ //== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -12,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/CharUnits.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -25,6 +25,7 @@ using namespace clang; using namespace ento; +using namespace taint; namespace { class ArrayBoundCheckerV2 : @@ -205,7 +206,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, // If we are under constrained and the index variables are tainted, report. if (state_exceedsUpperBound && state_withinUpperBound) { SVal ByteOffset = rawOffset.getByteOffset(); - if (state->isTainted(ByteOffset)) { + if (isTainted(state, ByteOffset)) { reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted, llvm::make_unique<TaintBugVisitor>(ByteOffset)); return; @@ -354,3 +355,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { mgr.registerChecker<ArrayBoundCheckerV2>(); } + +bool ento::shouldRegisterArrayBoundCheckerV2(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 577b5349f62e..e3fb4c3eb523 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -1,9 +1,8 @@ //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -1243,27 +1242,54 @@ void ento::registerNilArgChecker(CheckerManager &mgr) { mgr.registerChecker<NilArgChecker>(); } +bool ento::shouldRegisterNilArgChecker(const LangOptions &LO) { + return true; +} + void ento::registerCFNumberChecker(CheckerManager &mgr) { mgr.registerChecker<CFNumberChecker>(); } +bool ento::shouldRegisterCFNumberChecker(const LangOptions &LO) { + return true; +} + void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { mgr.registerChecker<CFRetainReleaseChecker>(); } +bool ento::shouldRegisterCFRetainReleaseChecker(const LangOptions &LO) { + return true; +} + void ento::registerClassReleaseChecker(CheckerManager &mgr) { mgr.registerChecker<ClassReleaseChecker>(); } +bool ento::shouldRegisterClassReleaseChecker(const LangOptions &LO) { + return true; +} + void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { mgr.registerChecker<VariadicMethodTypeChecker>(); } +bool ento::shouldRegisterVariadicMethodTypeChecker(const LangOptions &LO) { + return true; +} + void ento::registerObjCLoopChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCLoopChecker>(); } -void -ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { +bool ento::shouldRegisterObjCLoopChecker(const LangOptions &LO) { + return true; +} + +void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCNonNilReturnValueChecker>(); } + +bool ento::shouldRegisterObjCNonNilReturnValueChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index 00d08b371f37..009160fc9815 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -1,9 +1,8 @@ //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -183,3 +182,7 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection( void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { mgr.registerChecker<BlockInCriticalSectionChecker>(); } + +bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index 3008eddd397e..de8763c1b7b5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -1,9 +1,8 @@ //== BoolAssignmentChecker.cpp - Boolean assignment checker -----*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -155,3 +154,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, void ento::registerBoolAssignmentChecker(CheckerManager &mgr) { mgr.registerChecker<BoolAssignmentChecker>(); } + +bool ento::shouldRegisterBoolAssignmentChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index f98027942e18..10594e331cbe 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -1,9 +1,8 @@ //=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -15,6 +14,7 @@ #include "clang/Basic/Builtins.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" using namespace clang; @@ -24,30 +24,32 @@ namespace { class BuiltinFunctionChecker : public Checker<eval::Call> { public: - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; }; } -bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, +bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef state = C.getState(); - const FunctionDecl *FD = C.getCalleeDecl(CE); - const LocationContext *LCtx = C.getLocationContext(); + const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD) return false; + const LocationContext *LCtx = C.getLocationContext(); + const Expr *CE = Call.getOriginExpr(); + switch (FD->getBuiltinID()) { default: return false; case Builtin::BI__builtin_assume: { - assert (CE->arg_begin() != CE->arg_end()); - SVal ArgSVal = C.getSVal(CE->getArg(0)); - if (ArgSVal.isUndef()) + assert (Call.getNumArgs() > 0); + SVal Arg = Call.getArgSVal(0); + if (Arg.isUndef()) return true; // Return true to model purity. - state = state->assume(ArgSVal.castAs<DefinedOrUnknownSVal>(), true); + state = state->assume(Arg.castAs<DefinedOrUnknownSVal>(), true); // FIXME: do we want to warn here? Not right now. The most reports might // come from infeasible paths, thus being false positives. if (!state) { @@ -67,9 +69,9 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, // __builtin_assume_aligned, just return the value of the subexpression. // __builtin_addressof is going from a reference to a pointer, but those // are represented the same way in the analyzer. - assert (CE->arg_begin() != CE->arg_end()); - SVal X = C.getSVal(*(CE->arg_begin())); - C.addTransition(state->BindExpr(CE, LCtx, X)); + assert (Call.getNumArgs() > 0); + SVal Arg = Call.getArgSVal(0); + C.addTransition(state->BindExpr(CE, LCtx, Arg)); return true; } @@ -83,12 +85,14 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, // Set the extent of the region in bytes. This enables us to use the // SVal of the argument directly. If we save the extent in bits, we // cannot represent values like symbol*8. - auto Size = C.getSVal(*(CE->arg_begin())).castAs<DefinedOrUnknownSVal>(); + auto Size = Call.getArgSVal(0); + if (Size.isUndef()) + return true; // Return true to model purity. SValBuilder& svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); DefinedOrUnknownSVal extentMatchesSizeArg = - svalBuilder.evalEQ(state, Extent, Size); + svalBuilder.evalEQ(state, Extent, Size.castAs<DefinedOrUnknownSVal>()); state = state->assume(extentMatchesSizeArg, true); assert(state && "The region should not have any previous constraints"); @@ -96,21 +100,30 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, return true; } + case Builtin::BI__builtin_dynamic_object_size: case Builtin::BI__builtin_object_size: case Builtin::BI__builtin_constant_p: { // This must be resolvable at compile time, so we defer to the constant // evaluator for a value. + SValBuilder &SVB = C.getSValBuilder(); SVal V = UnknownVal(); Expr::EvalResult EVResult; if (CE->EvaluateAsInt(EVResult, C.getASTContext(), Expr::SE_NoSideEffects)) { // Make sure the result has the correct type. llvm::APSInt Result = EVResult.Val.getInt(); - SValBuilder &SVB = C.getSValBuilder(); BasicValueFactory &BVF = SVB.getBasicValueFactory(); BVF.getAPSIntType(CE->getType()).apply(Result); V = SVB.makeIntVal(Result); } + if (FD->getBuiltinID() == Builtin::BI__builtin_constant_p) { + // If we didn't manage to figure out if the value is constant or not, + // it is safe to assume that it's not constant and unsafe to assume + // that it's constant. + if (V.isUnknown()) + V = SVB.makeIntVal(0, CE->getType()); + } + C.addTransition(state->BindExpr(CE, LCtx, V)); return true; } @@ -120,3 +133,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) { mgr.registerChecker<BuiltinFunctionChecker>(); } + +bool ento::shouldRegisterBuiltinFunctionChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 8bffada69b9b..44f4530781a8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -1,9 +1,8 @@ //= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -18,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/STLExtras.h" @@ -58,7 +58,7 @@ public: static void *getTag() { static int tag; return &tag; } - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; @@ -73,7 +73,38 @@ public: typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *) const; + CallDescriptionMap<FnCheck> Callbacks = { + {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy}, + {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy}, + {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp}, + {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove}, + {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy}, + {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy}, + {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy}, + {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy}, + {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat}, + {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat}, + {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat}, + {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength}, + {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength}, + {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp}, + {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp}, + {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp}, + {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp}, + {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep}, + {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy}, + {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp}, + {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero}, + }; + + // These require a bit of special handling. + CallDescription StdCopy{{"std", "copy"}, 3}, + StdCopyBackward{{"std", "copy_backward"}, 3}; + FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const; void evalMemcpy(CheckerContext &C, const CallExpr *CE) const; void evalMempcpy(CheckerContext &C, const CallExpr *CE) const; void evalMemmove(CheckerContext &C, const CallExpr *CE) const; @@ -1201,9 +1232,6 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // void *memcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is the address of the destination buffer. const Expr *Dest = CE->getArg(0); @@ -1213,9 +1241,6 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is a pointer to the byte following the last written byte. const Expr *Dest = CE->getArg(0); @@ -1225,9 +1250,6 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // void *memmove(void *dst, const void *src, size_t n); // The return value is the address of the destination buffer. const Expr *Dest = CE->getArg(0); @@ -1237,18 +1259,12 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // void bcopy(const void *src, void *dst, size_t n); evalCopyCommon(C, CE, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0)); } void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // int memcmp(const void *s1, const void *s2, size_t n); CurrentFunctionDescription = "memory comparison function"; @@ -1323,18 +1339,12 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 1) - return; - // size_t strlen(const char *s); evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); } void CStringChecker::evalstrnLength(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 2) - return; - // size_t strnlen(const char *s, size_t maxlen); evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); } @@ -1459,9 +1469,6 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, } void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 2) - return; - // char *strcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1470,9 +1477,6 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // char *strncpy(char *restrict dst, const char *restrict src, size_t n); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1481,9 +1485,6 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 2) - return; - // char *stpcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ true, @@ -1492,9 +1493,6 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - // char *strlcpy(char *dst, const char *src, size_t n); evalStrcpyCommon(C, CE, /* returnEnd = */ true, @@ -1504,9 +1502,6 @@ void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 2) - return; - //char *strcat(char *restrict s1, const char *restrict s2); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1515,9 +1510,6 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - //char *strncat(char *restrict s1, const char *restrict s2, size_t n); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1526,8 +1518,9 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; + // FIXME: strlcat() uses a different rule for bound checking, i.e. 'n' means + // a different thing as compared to strncat(). This currently causes + // false positives in the alpha string bound checker. //char *strlcat(char *s1, const char *s2, size_t n); evalStrcpyCommon(C, CE, @@ -1881,35 +1874,23 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, } void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 2) - return; - //int strcmp(const char *s1, const char *s2); evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false); } void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - //int strncmp(const char *s1, const char *s2, size_t n); evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false); } void CStringChecker::evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 2) - return; - //int strcasecmp(const char *s1, const char *s2); evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true); } void CStringChecker::evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) - return; - //int strncasecmp(const char *s1, const char *s2, size_t n); evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true); } @@ -2043,9 +2024,6 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { //char *strsep(char **stringp, const char *delim); - if (CE->getNumArgs() < 2) - return; - // Sanity: does the search string parameter match the return type? const Expr *SearchStrPtr = CE->getArg(0); QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType(); @@ -2114,7 +2092,7 @@ void CStringChecker::evalStdCopyBackward(CheckerContext &C, void CStringChecker::evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() < 3) + if (!CE->getArg(2)->getType()->isPointerType()) return; ProgramStateRef State = C.getState(); @@ -2141,9 +2119,6 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C, } void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() != 3) - return; - CurrentFunctionDescription = "memory set function"; const Expr *Mem = CE->getArg(0); @@ -2192,9 +2167,6 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { - if (CE->getNumArgs() != 2) - return; - CurrentFunctionDescription = "memory clearance function"; const Expr *Mem = CE->getArg(0); @@ -2237,110 +2209,53 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { C.addTransition(State); } -static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { - IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return false; - - if (!AnalysisDeclContext::isInStdNamespace(FD)) - return false; - - if (II->getName().equals(Name)) - return true; - - return false; -} //===----------------------------------------------------------------------===// // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// -static CStringChecker::FnCheck identifyCall(const CallExpr *CE, - CheckerContext &C) { - const FunctionDecl *FDecl = C.getCalleeDecl(CE); - if (!FDecl) +CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return nullptr; + + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!FD) return nullptr; + if (Call.isCalled(StdCopy)) { + return &CStringChecker::evalStdCopy; + } else if (Call.isCalled(StdCopyBackward)) { + return &CStringChecker::evalStdCopyBackward; + } + // Pro-actively check that argument types are safe to do arithmetic upon. // We do not want to crash if someone accidentally passes a structure - // into, say, a C++ overload of any of these functions. - if (isCPPStdLibraryFunction(FDecl, "copy")) { - if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType()) + // into, say, a C++ overload of any of these functions. We could not check + // that for std::copy because they may have arguments of other types. + for (auto I : CE->arguments()) { + QualType T = I->getType(); + if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) return nullptr; - return &CStringChecker::evalStdCopy; - } else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) { - if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType()) - return nullptr; - return &CStringChecker::evalStdCopyBackward; - } else { - // An umbrella check for all C library functions. - for (auto I: CE->arguments()) { - QualType T = I->getType(); - if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) - return nullptr; - } } - // FIXME: Poorly-factored string switches are slow. - if (C.isCLibraryFunction(FDecl, "memcpy")) - return &CStringChecker::evalMemcpy; - else if (C.isCLibraryFunction(FDecl, "mempcpy")) - return &CStringChecker::evalMempcpy; - else if (C.isCLibraryFunction(FDecl, "memcmp")) - return &CStringChecker::evalMemcmp; - else if (C.isCLibraryFunction(FDecl, "memmove")) - return &CStringChecker::evalMemmove; - else if (C.isCLibraryFunction(FDecl, "memset") || - C.isCLibraryFunction(FDecl, "explicit_memset")) - return &CStringChecker::evalMemset; - else if (C.isCLibraryFunction(FDecl, "strcpy")) - return &CStringChecker::evalStrcpy; - else if (C.isCLibraryFunction(FDecl, "strncpy")) - return &CStringChecker::evalStrncpy; - else if (C.isCLibraryFunction(FDecl, "stpcpy")) - return &CStringChecker::evalStpcpy; - else if (C.isCLibraryFunction(FDecl, "strlcpy")) - return &CStringChecker::evalStrlcpy; - else if (C.isCLibraryFunction(FDecl, "strcat")) - return &CStringChecker::evalStrcat; - else if (C.isCLibraryFunction(FDecl, "strncat")) - return &CStringChecker::evalStrncat; - else if (C.isCLibraryFunction(FDecl, "strlcat")) - return &CStringChecker::evalStrlcat; - else if (C.isCLibraryFunction(FDecl, "strlen")) - return &CStringChecker::evalstrLength; - else if (C.isCLibraryFunction(FDecl, "strnlen")) - return &CStringChecker::evalstrnLength; - else if (C.isCLibraryFunction(FDecl, "strcmp")) - return &CStringChecker::evalStrcmp; - else if (C.isCLibraryFunction(FDecl, "strncmp")) - return &CStringChecker::evalStrncmp; - else if (C.isCLibraryFunction(FDecl, "strcasecmp")) - return &CStringChecker::evalStrcasecmp; - else if (C.isCLibraryFunction(FDecl, "strncasecmp")) - return &CStringChecker::evalStrncasecmp; - else if (C.isCLibraryFunction(FDecl, "strsep")) - return &CStringChecker::evalStrsep; - else if (C.isCLibraryFunction(FDecl, "bcopy")) - return &CStringChecker::evalBcopy; - else if (C.isCLibraryFunction(FDecl, "bcmp")) - return &CStringChecker::evalMemcmp; - else if (C.isCLibraryFunction(FDecl, "bzero") || - C.isCLibraryFunction(FDecl, "explicit_bzero")) - return &CStringChecker::evalBzero; + const FnCheck *Callback = Callbacks.lookup(Call); + if (Callback) + return *Callback; return nullptr; } -bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - - FnCheck evalFunction = identifyCall(CE, C); +bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { + FnCheck Callback = identifyCall(Call, C); // If the callee isn't a string function, let another checker handle it. - if (!evalFunction) + if (!Callback) return false; // Check and evaluate the call. - (this->*evalFunction)(C, CE); + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + (this->*Callback)(C, CE); // If the evaluate call resulted in no change, chain to the next eval call // handler. @@ -2476,18 +2391,26 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR, C.addTransition(state); } +void ento::registerCStringModeling(CheckerManager &Mgr) { + Mgr.registerChecker<CStringChecker>(); +} + +bool ento::shouldRegisterCStringModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ - CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \ + CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ checker->Filter.Check##name = true; \ checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + } \ + \ + bool ento::shouldRegister##name(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(CStringNullArg) REGISTER_CHECKER(CStringOutOfBounds) REGISTER_CHECKER(CStringBufferOverlap) REGISTER_CHECKER(CStringNotNullTerm) - - void ento::registerCStringCheckerBasic(CheckerManager &Mgr) { - Mgr.registerChecker<CStringChecker>(); - } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp index bbeb41c5f3cf..b828ac059236 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -1,9 +1,8 @@ //== CStringSyntaxChecker.cpp - CoreFoundation containers API *- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -154,8 +153,6 @@ bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { if (CE->getNumArgs() != 3) return false; - const FunctionDecl *FD = CE->getDirectCallee(); - bool Append = CheckerContext::isCLibraryFunction(FD, "strlcat"); const Expr *DstArg = CE->getArg(0); const Expr *LenArg = CE->getArg(2); @@ -195,13 +192,8 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { ASTContext &C = BR.getContext(); uint64_t BufferLen = C.getTypeSize(Buffer) / 8; auto RemainingBufferLen = BufferLen - DstOff; - if (Append) { - if (RemainingBufferLen <= ILRawVal) - return true; - } else { - if (RemainingBufferLen < ILRawVal) - return true; - } + if (RemainingBufferLen < ILRawVal) + return true; } } } @@ -290,3 +282,6 @@ void ento::registerCStringSyntaxChecker(CheckerManager &mgr) { mgr.registerChecker<CStringSyntaxChecker>(); } +bool ento::shouldRegisterCStringSyntaxChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp index 0b539e1188eb..01f5b9c889e3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp @@ -1,9 +1,8 @@ //=== CXXSelfAssignmentChecker.cpp -----------------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -51,12 +50,32 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const { State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx)); auto ParamVal = State->getSVal(Param); + ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx); - C.addTransition(SelfAssignState); + const NoteTag *SelfAssignTag = + C.getNoteTag([MD](BugReport &BR) -> std::string { + SmallString<256> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << "Assuming " << MD->getParamDecl(0)->getName() << " == *this"; + return Out.str(); + }); + C.addTransition(SelfAssignState, SelfAssignTag); + ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx); - C.addTransition(NonSelfAssignState); + const NoteTag *NonSelfAssignTag = + C.getNoteTag([MD](BugReport &BR) -> std::string { + SmallString<256> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << "Assuming " << MD->getParamDecl(0)->getName() << " != *this"; + return Out.str(); + }); + C.addTransition(NonSelfAssignState, NonSelfAssignTag); } void ento::registerCXXSelfAssignmentChecker(CheckerManager &Mgr) { Mgr.registerChecker<CXXSelfAssignmentChecker>(); } + +bool ento::shouldRegisterCXXSelfAssignmentChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index ef30dc74c39d..5a7eba0760fe 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -1,9 +1,8 @@ //===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -29,14 +28,6 @@ using namespace ento; namespace { -struct ChecksFilter { - DefaultBool Check_CallAndMessageUnInitRefArg; - DefaultBool Check_CallAndMessageChecker; - - CheckName CheckName_CallAndMessageUnInitRefArg; - CheckName CheckName_CallAndMessageChecker; -}; - class CallAndMessageChecker : public Checker< check::PreStmt<CallExpr>, check::PreStmt<CXXDeleteExpr>, @@ -57,7 +48,8 @@ class CallAndMessageChecker mutable std::unique_ptr<BugType> BT_call_few_args; public: - ChecksFilter Filter; + DefaultBool Check_CallAndMessageUnInitRefArg; + CheckName CheckName_CallAndMessageUnInitRefArg; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; @@ -152,7 +144,7 @@ bool CallAndMessageChecker::uninitRefOrPointer( CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, int ArgumentNumber) const { - if (!Filter.Check_CallAndMessageUnInitRefArg) + if (!Check_CallAndMessageUnInitRefArg) return false; // No parameter declaration available, i.e. variadic function argument. @@ -608,13 +600,20 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, C.addTransition(state); } -#define REGISTER_CHECKER(name) \ - void ento::register##name(CheckerManager &mgr) { \ - CallAndMessageChecker *Checker = \ - mgr.registerChecker<CallAndMessageChecker>(); \ - Checker->Filter.Check_##name = true; \ - Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \ - } +void ento::registerCallAndMessageChecker(CheckerManager &mgr) { + mgr.registerChecker<CallAndMessageChecker>(); +} + +bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) { + return true; +} -REGISTER_CHECKER(CallAndMessageUnInitRefArg) -REGISTER_CHECKER(CallAndMessageChecker) +void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) { + CallAndMessageChecker *Checker = mgr.getChecker<CallAndMessageChecker>(); + Checker->Check_CallAndMessageUnInitRefArg = true; + Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckName(); +} + +bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 5deb62d32311..05ece961467f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -1,9 +1,8 @@ //=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -140,10 +139,13 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { } void ento::registerCastSizeChecker(CheckerManager &mgr) { + mgr.registerChecker<CastSizeChecker>(); +} + +bool ento::shouldRegisterCastSizeChecker(const LangOptions &LO) { // PR31226: C++ is more complicated than what this checker currently supports. // There are derived-to-base casts, there are different rules for 0-size // structures, no flexible arrays, etc. // FIXME: Disabled on C++ for now. - if (!mgr.getLangOpts().CPlusPlus) - mgr.registerChecker<CastSizeChecker>(); + return !LO.CPlusPlus; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index 2bd3879627cb..93665596be29 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -1,9 +1,8 @@ //=== CastToStructChecker.cpp ----------------------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -120,3 +119,7 @@ public: void ento::registerCastToStructChecker(CheckerManager &mgr) { mgr.registerChecker<CastToStructChecker>(); } + +bool ento::shouldRegisterCastToStructChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp new file mode 100644 index 000000000000..ff5d12c27c69 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -0,0 +1,190 @@ +//===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines CastValueChecker which models casts of custom RTTIs. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/Optional.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastValueChecker : public Checker<eval::Call> { + using CastCheck = + std::function<void(const CastValueChecker *, const CallExpr *, + DefinedOrUnknownSVal, CheckerContext &)>; + +public: + // We have three cases to evaluate a cast: + // 1) The parameter is non-null, the return value is non-null + // 2) The parameter is non-null, the return value is null + // 3) The parameter is null, the return value is null + // + // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +private: + // These are known in the LLVM project. + const CallDescriptionMap<CastCheck> CDM = { + {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast}, + {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast}, + {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull}, + {{{"llvm", "dyn_cast_or_null"}, 1}, + &CastValueChecker::evalDynCastOrNull}}; + + void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; + void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; + void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; + void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; +}; +} // namespace + +static std::string getCastName(const Expr *Cast) { + return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString(); +} + +static void evalNonNullParamNonNullReturn(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) { + ProgramStateRef State = C.getState()->assume(ParamDV, true); + if (!State) + return; + + State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false); + + std::string CastFromName = getCastName(CE->getArg(0)); + std::string CastToName = getCastName(CE); + + const NoteTag *CastTag = C.getNoteTag( + [CastFromName, CastToName](BugReport &) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + Out << "Assuming dynamic cast from '" << CastFromName << "' to '" + << CastToName << "' succeeds"; + return Out.str(); + }, + /*IsPrunable=*/true); + + C.addTransition(State, CastTag); +} + +static void evalNonNullParamNullReturn(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) { + ProgramStateRef State = C.getState()->assume(ParamDV, true); + if (!State) + return; + + State = State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNull(), false); + + std::string CastFromName = getCastName(CE->getArg(0)); + std::string CastToName = getCastName(CE); + + const NoteTag *CastTag = C.getNoteTag( + [CastFromName, CastToName](BugReport &) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + Out << "Assuming dynamic cast from '" << CastFromName << "' to '" + << CastToName << "' fails"; + return Out.str(); + }, + /*IsPrunable=*/true); + + C.addTransition(State, CastTag); +} + +static void evalNullParamNullReturn(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) { + ProgramStateRef State = C.getState()->assume(ParamDV, false); + if (!State) + return; + + State = State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNull(), false); + + const NoteTag *CastTag = + C.getNoteTag("Assuming null pointer is passed into cast", + /*IsPrunable=*/true); + + C.addTransition(State, CastTag); +} + +void CastValueChecker::evalCast(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); +} + +void CastValueChecker::evalDynCast(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNonNullParamNullReturn(CE, ParamDV, C); +} + +void CastValueChecker::evalCastOrNull(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNullParamNullReturn(CE, ParamDV, C); +} + +void CastValueChecker::evalDynCastOrNull(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNonNullParamNullReturn(CE, ParamDV, C); + evalNullParamNullReturn(CE, ParamDV, C); +} + +bool CastValueChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + const CastCheck *Check = CDM.lookup(Call); + if (!Check) + return false; + + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + if (!CE) + return false; + + // If we cannot obtain both of the classes we cannot be sure how to model it. + if (!CE->getType()->getPointeeCXXRecordDecl() || + !CE->getArg(0)->getType()->getPointeeCXXRecordDecl()) + return false; + + SVal ParamV = Call.getArgSVal(0); + auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>(); + if (!ParamDV) + return false; + + (*Check)(this, CE, *ParamDV, C); + return true; +} + +void ento::registerCastValueChecker(CheckerManager &Mgr) { + Mgr.registerChecker<CastValueChecker>(); +} + +bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 00a912f27a8d..a7ca814c8f96 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -1,9 +1,8 @@ //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -1087,10 +1086,10 @@ bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain( } void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { - const LangOptions &LangOpts = Mgr.getLangOpts(); - // These checker only makes sense under MRR. - if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) - return; - Mgr.registerChecker<ObjCDeallocChecker>(); } + +bool ento::shouldRegisterObjCDeallocChecker(const LangOptions &LO) { + // These checker only makes sense under MRR. + return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index fe6715595e6f..a020d33bfd95 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -1,9 +1,8 @@ -//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// +//===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -138,3 +137,7 @@ public: void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCMethSigsChecker>(); } + +bool ento::shouldRegisterObjCMethSigsChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 163ca9d8556f..3f1c213a5647 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -1,9 +1,8 @@ //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -45,6 +44,7 @@ struct ChecksFilter { DefaultBool check_mktemp; DefaultBool check_mkstemp; DefaultBool check_strcpy; + DefaultBool check_DeprecatedOrUnsafeBufferHandling; DefaultBool check_rand; DefaultBool check_vfork; DefaultBool check_FloatLoopCounter; @@ -58,6 +58,7 @@ struct ChecksFilter { CheckName checkName_mktemp; CheckName checkName_mkstemp; CheckName checkName_strcpy; + CheckName checkName_DeprecatedOrUnsafeBufferHandling; CheckName checkName_rand; CheckName checkName_vfork; CheckName checkName_FloatLoopCounter; @@ -104,6 +105,8 @@ public: void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); + void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, + const FunctionDecl *FD); void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); @@ -149,6 +152,14 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { .Case("mkstemps", &WalkAST::checkCall_mkstemp) .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) + .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", + "vscanf", "vwscanf", "vfscanf", "vfwscanf", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", + "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Cases("strncpy", "strncat", "memset", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) .Case("drand48", &WalkAST::checkCall_rand) .Case("erand48", &WalkAST::checkCall_rand) .Case("jrand48", &WalkAST::checkCall_rand) @@ -553,7 +564,6 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { CELoc, CE->getCallee()->getSourceRange()); } - //===----------------------------------------------------------------------===// // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. //===----------------------------------------------------------------------===// @@ -642,6 +652,7 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { // CWE-119: Improper Restriction of Operations within // the Bounds of a Memory Buffer //===----------------------------------------------------------------------===// + void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { if (!filter.check_strcpy) return; @@ -680,6 +691,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { // CWE-119: Improper Restriction of Operations within // the Bounds of a Memory Buffer //===----------------------------------------------------------------------===// + void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { if (!filter.check_strcpy) return; @@ -702,8 +714,92 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { } //===----------------------------------------------------------------------===// +// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', +// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', +// 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', +// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset' +// is deprecated since C11. +// +// Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf', +// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', +// 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations +// is insecure. +// +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer +//===----------------------------------------------------------------------===// + +void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, + const FunctionDecl *FD) { + if (!filter.check_DeprecatedOrUnsafeBufferHandling) + return; + + if (!BR.getContext().getLangOpts().C11) + return; + + // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size + // restrictions). + enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; + + StringRef Name = FD->getIdentifier()->getName(); + if (Name.startswith("__builtin_")) + Name = Name.substr(10); + + int ArgIndex = + llvm::StringSwitch<int>(Name) + .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) + .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", + "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) + .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", + "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) + .Default(UNKNOWN_CALL); + + assert(ArgIndex != UNKNOWN_CALL && "Unsupported function"); + bool BoundsProvided = ArgIndex == DEPR_ONLY; + + if (!BoundsProvided) { + // Currently we only handle (not wide) string literals. It is possible to do + // better, either by looking at references to const variables, or by doing + // real flow analysis. + auto FormatString = + dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts()); + if (FormatString && + FormatString->getString().find("%s") == StringRef::npos && + FormatString->getString().find("%[") == StringRef::npos) + BoundsProvided = true; + } + + SmallString<128> Buf1; + SmallString<512> Buf2; + llvm::raw_svector_ostream Out1(Buf1); + llvm::raw_svector_ostream Out2(Buf2); + + Out1 << "Potential insecure memory buffer bounds restriction in call '" + << Name << "'"; + Out2 << "Call to function '" << Name + << "' is insecure as it does not provide "; + + if (!BoundsProvided) { + Out2 << "bounding of the memory buffer or "; + } + + Out2 << "security checks introduced " + "in the C11 standard. Replace with analogous functions that " + "support length arguments or provides boundary checks such as '" + << Name << "_s' in case of C11"; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), + filter.checkName_DeprecatedOrUnsafeBufferHandling, + Out1.str(), "Security", Out2.str(), CELoc, + CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// // Common check for str* functions with no bounds parameters. //===----------------------------------------------------------------------===// + bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); if (!FPT) @@ -906,12 +1002,23 @@ public: }; } +void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { + mgr.registerChecker<SecuritySyntaxChecker>(); +} + +bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ - SecuritySyntaxChecker *checker = \ - mgr.registerChecker<SecuritySyntaxChecker>(); \ + SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ checker->filter.check_##name = true; \ checker->filter.checkName_##name = mgr.getCurrentCheckName(); \ + } \ + \ + bool ento::shouldRegister##name(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(bcmp) @@ -926,5 +1033,4 @@ REGISTER_CHECKER(rand) REGISTER_CHECKER(vfork) REGISTER_CHECKER(FloatLoopCounter) REGISTER_CHECKER(UncheckedReturn) - - +REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index 7688b713b06b..ec401cfa8985 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -1,9 +1,8 @@ //==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -91,3 +90,7 @@ public: void ento::registerSizeofPointerChecker(CheckerManager &mgr) { mgr.registerChecker<SizeofPointerChecker>(); } + +bool ento::shouldRegisterSizeofPointerChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 44fac0278bdd..3e5e2b913914 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -1,9 +1,8 @@ //===- CheckerDocumentation.cpp - Documentation checker ---------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -216,7 +215,7 @@ public: /// Evaluates function call. /// - /// The analysis core threats all function calls in the same way. However, some + /// The analysis core treats all function calls in the same way. However, some /// functions have special meaning, which should be reflected in the program /// state. This callback allows a checker to provide domain specific knowledge /// about the particular functions it knows about. diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 673608db1a1d..9fffedfccd87 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -1,9 +1,8 @@ -//===- Chrootchecker.cpp -------- Basic security checks ---------*- C++ -*-===// +//===-- ChrootChecker.cpp - chroot usage checks ---------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -15,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -38,53 +38,44 @@ bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; } // ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)-- // | | // bug<--foo()-- JAIL_ENTERED<--foo()-- -class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > { - mutable IdentifierInfo *II_chroot, *II_chdir; +class ChrootChecker : public Checker<eval::Call, check::PreCall> { // This bug refers to possibly break out of a chroot() jail. mutable std::unique_ptr<BuiltinBug> BT_BreakJail; + const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1}; + public: - ChrootChecker() : II_chroot(nullptr), II_chdir(nullptr) {} + ChrootChecker() {} static void *getTag() { static int x; return &x; } - bool evalCall(const CallExpr *CE, CheckerContext &C) const; - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: - void Chroot(CheckerContext &C, const CallExpr *CE) const; - void Chdir(CheckerContext &C, const CallExpr *CE) const; + void evalChroot(const CallEvent &Call, CheckerContext &C) const; + void evalChdir(const CallEvent &Call, CheckerContext &C) const; }; } // end anonymous namespace -bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD) - return false; - - ASTContext &Ctx = C.getASTContext(); - if (!II_chroot) - II_chroot = &Ctx.Idents.get("chroot"); - if (!II_chdir) - II_chdir = &Ctx.Idents.get("chdir"); - - if (FD->getIdentifier() == II_chroot) { - Chroot(C, CE); +bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { + if (Call.isCalled(Chroot)) { + evalChroot(Call, C); return true; } - if (FD->getIdentifier() == II_chdir) { - Chdir(C, CE); + if (Call.isCalled(Chdir)) { + evalChdir(Call, C); return true; } return false; } -void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { +void ChrootChecker::evalChroot(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); @@ -94,7 +85,7 @@ void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { C.addTransition(state); } -void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { +void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); @@ -104,7 +95,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { return; // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. - const Expr *ArgExpr = CE->getArg(0); + const Expr *ArgExpr = Call.getArgExpr(0); SVal ArgVal = C.getSVal(ArgExpr); if (const MemRegion *R = ArgVal.getAsRegion()) { @@ -121,19 +112,10 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { } // Check the jail state before any function call except chroot and chdir(). -void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD) - return; - - ASTContext &Ctx = C.getASTContext(); - if (!II_chroot) - II_chroot = &Ctx.Idents.get("chroot"); - if (!II_chdir) - II_chdir = &Ctx.Idents.get("chdir"); - +void ChrootChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { // Ignore chroot and chdir. - if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir) + if (Call.isCalled(Chroot) || Call.isCalled(Chdir)) return; // If jail state is ROOT_CHANGED, generate BugReport. @@ -153,3 +135,7 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { void ento::registerChrootChecker(CheckerManager &mgr) { mgr.registerChecker<ChrootChecker>(); } + +bool ento::shouldRegisterChrootChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp index 89354b866004..4fc225056d4c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp @@ -1,9 +1,8 @@ //===--- CloneChecker.cpp - Clone detection checker -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -28,6 +27,13 @@ using namespace ento; namespace { class CloneChecker : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> { +public: + // Checker options. + int MinComplexity; + bool ReportNormalClones; + StringRef IgnoredFilesPattern; + +private: mutable CloneDetector Detector; mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious; @@ -63,19 +69,6 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU, // At this point, every statement in the translation unit has been analyzed by // the CloneDetector. The only thing left to do is to report the found clones. - int MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption( - "MinimumCloneComplexity", 50, this); - assert(MinComplexity >= 0); - - bool ReportSuspiciousClones = Mgr.getAnalyzerOptions() - .getCheckerBooleanOption("ReportSuspiciousClones", true, this); - - bool ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption( - "ReportNormalClones", true, this); - - StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions() - .getCheckerStringOption("IgnoredFilesPattern", "", this); - // Let the CloneDetector create a list of clones from all the analyzed // statements. We don't filter for matching variable patterns at this point // because reportSuspiciousClones() wants to search them for errors. @@ -87,8 +80,7 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU, MinComplexityConstraint(MinComplexity), RecursiveCloneTypeIIVerifyConstraint(), OnlyLargestCloneConstraint()); - if (ReportSuspiciousClones) - reportSuspiciousClones(BR, Mgr, AllCloneGroups); + reportSuspiciousClones(BR, Mgr, AllCloneGroups); // We are done for this translation unit unless we also need to report normal // clones. @@ -200,5 +192,22 @@ void CloneChecker::reportSuspiciousClones( //===----------------------------------------------------------------------===// void ento::registerCloneChecker(CheckerManager &Mgr) { - Mgr.registerChecker<CloneChecker>(); + auto *Checker = Mgr.registerChecker<CloneChecker>(); + + Checker->MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption( + Checker, "MinimumCloneComplexity"); + + if (Checker->MinComplexity < 0) + Mgr.reportInvalidCheckerOptionValue( + Checker, "MinimumCloneComplexity", "a non-negative value"); + + Checker->ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Checker, "ReportNormalClones"); + + Checker->IgnoredFilesPattern = Mgr.getAnalyzerOptions() + .getCheckerStringOption(Checker, "IgnoredFilesPattern"); +} + +bool ento::shouldRegisterCloneChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp index a5c67c2a5b45..5058d101b8e5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -1,9 +1,8 @@ //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -196,3 +195,7 @@ bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, void ento::registerConversionChecker(CheckerManager &mgr) { mgr.registerChecker<ConversionChecker>(); } + +bool ento::shouldRegisterConversionChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 4e0f6d3bedfd..d5baa2bcba6f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -1,9 +1,8 @@ //==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -161,6 +160,26 @@ public: return InEH->count(D); } + bool isSuppressed(SourceRange R) { + SourceManager &SMgr = Ctx.getSourceManager(); + SourceLocation Loc = R.getBegin(); + if (!Loc.isValid()) + return false; + + FileID FID = SMgr.getFileID(Loc); + bool Invalid = false; + StringRef Data = SMgr.getBufferData(FID, &Invalid); + if (Invalid) + return false; + + // Files autogenerated by DriverKit IIG contain some dead stores that + // we don't want to report. + if (Data.startswith("/* iig")) + return true; + + return false; + } + void Report(const VarDecl *V, DeadStoreKind dsk, PathDiagnosticLocation L, SourceRange R) { if (Escaped.count(V)) @@ -176,6 +195,9 @@ public: if (!reachableCode->isReachable(currentBlock)) return; + if (isSuppressed(R)) + return; + SmallString<64> buf; llvm::raw_svector_ostream os(buf); const char *BugType = nullptr; @@ -479,3 +501,7 @@ public: void ento::registerDeadStoresChecker(CheckerManager &mgr) { mgr.registerChecker<DeadStoresChecker>(); } + +bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 90b1111aff0f..bb9e8110b647 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -1,9 +1,8 @@ //==- DebugCheckers.cpp - Debugging Checkers ---------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -36,9 +35,9 @@ public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) { - DominatorTree dom; - dom.buildDominatorTree(*AC); - dom.dump(); + CFGDomTree Dom; + Dom.buildDominatorTree(AC->getCFG()); + Dom.dump(); } } }; @@ -48,6 +47,61 @@ void ento::registerDominatorsTreeDumper(CheckerManager &mgr) { mgr.registerChecker<DominatorsTreeDumper>(); } +bool ento::shouldRegisterDominatorsTreeDumper(const LangOptions &LO) { + return true; +} + +//===----------------------------------------------------------------------===// +// PostDominatorsTreeDumper +//===----------------------------------------------------------------------===// + +namespace { +class PostDominatorsTreeDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) { + CFGPostDomTree Dom; + Dom.buildDominatorTree(AC->getCFG()); + Dom.dump(); + } + } +}; +} + +void ento::registerPostDominatorsTreeDumper(CheckerManager &mgr) { + mgr.registerChecker<PostDominatorsTreeDumper>(); +} + +bool ento::shouldRegisterPostDominatorsTreeDumper(const LangOptions &LO) { + return true; +} + +//===----------------------------------------------------------------------===// +// ControlDependencyTreeDumper +//===----------------------------------------------------------------------===// + +namespace { +class ControlDependencyTreeDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) { + ControlDependencyCalculator Dom(AC->getCFG()); + Dom.dump(); + } + } +}; +} + +void ento::registerControlDependencyTreeDumper(CheckerManager &mgr) { + mgr.registerChecker<ControlDependencyTreeDumper>(); +} + +bool ento::shouldRegisterControlDependencyTreeDumper(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // LiveVariablesDumper //===----------------------------------------------------------------------===// @@ -68,6 +122,10 @@ void ento::registerLiveVariablesDumper(CheckerManager &mgr) { mgr.registerChecker<LiveVariablesDumper>(); } +bool ento::shouldRegisterLiveVariablesDumper(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // LiveStatementsDumper //===----------------------------------------------------------------------===// @@ -87,6 +145,10 @@ void ento::registerLiveStatementsDumper(CheckerManager &mgr) { mgr.registerChecker<LiveStatementsDumper>(); } +bool ento::shouldRegisterLiveStatementsDumper(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // CFGViewer //===----------------------------------------------------------------------===// @@ -107,6 +169,10 @@ void ento::registerCFGViewer(CheckerManager &mgr) { mgr.registerChecker<CFGViewer>(); } +bool ento::shouldRegisterCFGViewer(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // CFGDumper //===----------------------------------------------------------------------===// @@ -133,6 +199,10 @@ void ento::registerCFGDumper(CheckerManager &mgr) { mgr.registerChecker<CFGDumper>(); } +bool ento::shouldRegisterCFGDumper(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // CallGraphViewer //===----------------------------------------------------------------------===// @@ -153,6 +223,10 @@ void ento::registerCallGraphViewer(CheckerManager &mgr) { mgr.registerChecker<CallGraphViewer>(); } +bool ento::shouldRegisterCallGraphViewer(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // CallGraphDumper //===----------------------------------------------------------------------===// @@ -173,6 +247,9 @@ void ento::registerCallGraphDumper(CheckerManager &mgr) { mgr.registerChecker<CallGraphDumper>(); } +bool ento::shouldRegisterCallGraphDumper(const LangOptions &LO) { + return true; +} //===----------------------------------------------------------------------===// // ConfigDumper @@ -214,6 +291,10 @@ void ento::registerConfigDumper(CheckerManager &mgr) { mgr.registerChecker<ConfigDumper>(); } +bool ento::shouldRegisterConfigDumper(const LangOptions &LO) { + return true; +} + //===----------------------------------------------------------------------===// // ExplodedGraph Viewer //===----------------------------------------------------------------------===// @@ -233,3 +314,37 @@ void ento::registerExplodedGraphViewer(CheckerManager &mgr) { mgr.registerChecker<ExplodedGraphViewer>(); } +bool ento::shouldRegisterExplodedGraphViewer(const LangOptions &LO) { + return true; +} + +//===----------------------------------------------------------------------===// +// Emits a report for every Stmt that the analyzer visits. +//===----------------------------------------------------------------------===// + +namespace { + +class ReportStmts : public Checker<check::PreStmt<Stmt>> { + BuiltinBug BT_stmtLoc{this, "Statement"}; + +public: + void checkPreStmt(const Stmt *S, CheckerContext &C) const { + ExplodedNode *Node = C.generateNonFatalErrorNode(); + if (!Node) + return; + + auto Report = llvm::make_unique<BugReport>(BT_stmtLoc, "Statement", Node); + + C.emitReport(std::move(Report)); + } +}; + +} // end of anonymous namespace + +void ento::registerReportStmts(CheckerManager &mgr) { + mgr.registerChecker<ReportStmts>(); +} + +bool ento::shouldRegisterReportStmts(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp index adf5a8e77a74..8bf77c109f8a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp @@ -1,9 +1,8 @@ //===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -148,3 +147,8 @@ DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); } + +bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( + const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index d01a889d256a..2c264833f2a9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -1,9 +1,8 @@ -//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// +//===-- DereferenceChecker.cpp - Null dereference checker -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -304,3 +303,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, void ento::registerDereferenceChecker(CheckerManager &mgr) { mgr.registerChecker<DereferenceChecker>(); } + +bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp index 2a559422df34..0058f3d3881f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -1,9 +1,8 @@ //=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -206,12 +205,6 @@ void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( } } -// Register the checker that checks for direct accesses in all functions, -// except for the initialization and copy routines. -void ento::registerDirectIvarAssignment(CheckerManager &mgr) { - mgr.registerChecker<DirectIvarAssignment>(); -} - // Register the checker that checks for direct accesses in functions annotated // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))). static bool AttrFilter(const ObjCMethodDecl *M) { @@ -221,7 +214,22 @@ static bool AttrFilter(const ObjCMethodDecl *M) { return true; } +// Register the checker that checks for direct accesses in all functions, +// except for the initialization and copy routines. +void ento::registerDirectIvarAssignment(CheckerManager &mgr) { + mgr.registerChecker<DirectIvarAssignment>(); +} + +bool ento::shouldRegisterDirectIvarAssignment(const LangOptions &LO) { + return true; +} + void ento::registerDirectIvarAssignmentForAnnotatedFunctions( CheckerManager &mgr) { - mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter; + mgr.getChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter; +} + +bool ento::shouldRegisterDirectIvarAssignmentForAnnotatedFunctions( + const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index a220a0513e28..33e8fcd8af7b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -1,9 +1,8 @@ //== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -12,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -20,6 +20,7 @@ using namespace clang; using namespace ento; +using namespace taint; namespace { class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > { @@ -84,10 +85,10 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, return; } - bool TaintedD = C.getState()->isTainted(*DV); + bool TaintedD = isTainted(C.getState(), *DV); if ((stateNotZero && stateZero && TaintedD)) { reportBug("Division by a tainted value, possibly zero", stateZero, C, - llvm::make_unique<TaintBugVisitor>(*DV)); + llvm::make_unique<taint::TaintBugVisitor>(*DV)); return; } @@ -99,3 +100,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, void ento::registerDivZeroChecker(CheckerManager &mgr) { mgr.registerChecker<DivZeroChecker>(); } + +bool ento::shouldRegisterDivZeroChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp index 803d7ae22a71..4d979dc9f240 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp @@ -1,9 +1,8 @@ //== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -206,3 +205,7 @@ void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE, void ento::registerDynamicTypeChecker(CheckerManager &mgr) { mgr.registerChecker<DynamicTypeChecker>(); } + +bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 31d4eebe8968..3cfe4dc82a10 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -1,9 +1,8 @@ //===- DynamicTypePropagation.cpp ------------------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -115,8 +114,8 @@ public: void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { ProgramStateRef State = C.getState(); - DynamicTypeMapImpl TypeMap = State->get<DynamicTypeMap>(); - for (DynamicTypeMapImpl::iterator I = TypeMap.begin(), E = TypeMap.end(); + DynamicTypeMapTy TypeMap = State->get<DynamicTypeMap>(); + for (DynamicTypeMapTy::iterator I = TypeMap.begin(), E = TypeMap.end(); I != E; ++I) { if (!SR.isLiveRegion(I->first)) { State = State->remove<DynamicTypeMap>(I->first); @@ -145,7 +144,7 @@ static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); ProgramStateRef State = C.getState(); - State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubclass=*/false); + State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubClassed=*/false); C.addTransition(State); } @@ -308,7 +307,7 @@ void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, return; C.addTransition(setDynamicTypeInfo(C.getState(), MR, NewE->getType(), - /*CanBeSubclass=*/false)); + /*CanBeSubClassed=*/false)); } const ObjCObjectType * @@ -888,7 +887,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, // MostSpecializedTypeArgsMap. We should only store anything in the later if // the stored data differs from the one stored in the former. State = setDynamicTypeInfo(State, RetRegion, ResultType, - /*CanBeSubclass=*/true); + /*CanBeSubClassed=*/true); Pred = C.addTransition(State); } @@ -988,11 +987,18 @@ DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N, /// Register checkers. void ento::registerObjCGenericsChecker(CheckerManager &mgr) { - DynamicTypePropagation *checker = - mgr.registerChecker<DynamicTypePropagation>(); + DynamicTypePropagation *checker = mgr.getChecker<DynamicTypePropagation>(); checker->CheckGenerics = true; } +bool ento::shouldRegisterObjCGenericsChecker(const LangOptions &LO) { + return true; +} + void ento::registerDynamicTypePropagation(CheckerManager &mgr) { mgr.registerChecker<DynamicTypePropagation>(); } + +bool ento::shouldRegisterDynamicTypePropagation(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp index 4e51cffaa744..a6539098c89a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -1,9 +1,8 @@ //===- EnumCastOutOfRangeChecker.cpp ---------------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -92,6 +91,22 @@ void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const { void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, CheckerContext &C) const { + + // Only perform enum range check on casts where such checks are valid. For + // all other cast kinds (where enum range checks are unnecessary or invalid), + // just return immediately. TODO: The set of casts whitelisted for enum + // range checking may be incomplete. Better to add a missing cast kind to + // enable a missing check than to generate false negatives and have to remove + // those later. + switch (CE->getCastKind()) { + case CK_IntegralCast: + break; + + default: + return; + break; + } + // Get the value of the expression to cast. const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); @@ -126,3 +141,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) { mgr.registerChecker<EnumCastOutOfRangeChecker>(); } + +bool ento::shouldRegisterEnumCastOutOfRangeChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 2553f54bbcac..f23f784016d8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -1,9 +1,8 @@ //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -12,6 +11,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ScopedPrinter.h" @@ -54,7 +54,7 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, ExplodedNode *N) const; public: - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const; @@ -64,8 +64,12 @@ public: REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *) -bool ExprInspectionChecker::evalCall(const CallExpr *CE, +bool ExprInspectionChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { + const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return false; + // These checks should have no effect on the surrounding environment // (globals should not be invalidated, etc), hence the use of evalCall. FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) @@ -409,3 +413,7 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, void ento::registerExprInspectionChecker(CheckerManager &Mgr) { Mgr.registerChecker<ExprInspectionChecker>(); } + +bool ento::shouldRegisterExprInspectionChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 165a4e4490eb..94542be7dd7c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -1,9 +1,8 @@ //=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -65,3 +64,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, void ento::registerFixedAddressChecker(CheckerManager &mgr) { mgr.registerChecker<FixedAddressChecker>(); } + +bool ento::shouldRegisterFixedAddressChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp index 248b9c3f7693..d471c23b83bf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp @@ -1,9 +1,8 @@ //===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -197,7 +196,7 @@ static void emitDiagnostics(const BoundNodes &Nodes, ADC->getDecl(), Checker, /*Name=*/"GCD performance anti-pattern", - /*Category=*/"Performance", + /*BugCategory=*/"Performance", OS.str(), PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC), SW->getSourceRange()); @@ -222,8 +221,12 @@ void GCDAntipatternChecker::checkASTCodeBody(const Decl *D, emitDiagnostics(Match, "group", BR, ADC, this); } -} +} // end of anonymous namespace void ento::registerGCDAntipattern(CheckerManager &Mgr) { Mgr.registerChecker<GCDAntipatternChecker>(); } + +bool ento::shouldRegisterGCDAntipattern(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp index 818716dd6070..f4308f510f0b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp @@ -1,9 +1,8 @@ //==- GTestChecker.cpp - Model gtest API --*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -289,11 +288,11 @@ ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2, } void ento::registerGTestChecker(CheckerManager &Mgr) { - const LangOptions &LangOpts = Mgr.getLangOpts(); + Mgr.registerChecker<GTestChecker>(); +} + +bool ento::shouldRegisterGTestChecker(const LangOptions &LO) { // gtest is a C++ API so there is no sense running the checker // if not compiling for C++. - if (!LangOpts.CPlusPlus) - return; - - Mgr.registerChecker<GTestChecker>(); + return LO.CPlusPlus; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 32fed202d3ab..d3ab98033236 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -1,9 +1,8 @@ //== GenericTaintChecker.cpp ----------------------------------- -*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,6 +13,8 @@ // aggressively, even if the involved symbols are under constrained. // //===----------------------------------------------------------------------===// + +#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/Attr.h" #include "clang/Basic/Builtins.h" @@ -23,9 +24,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include <climits> +#include <initializer_list> +#include <utility> using namespace clang; using namespace ento; +using namespace taint; namespace { class GenericTaintChecker @@ -40,13 +44,16 @@ public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; + private: static const unsigned InvalidArgIndex = UINT_MAX; /// Denotes the return vale. static const unsigned ReturnValueIndex = UINT_MAX - 1; mutable std::unique_ptr<BugType> BT; - inline void initBugType() const { + void initBugType() const { if (!BT) BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data")); } @@ -61,9 +68,6 @@ private: /// Propagate taint generated at pre-visit. bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const; - /// Add taint sources on a post visit. - void addSourcesPost(const CallExpr *CE, CheckerContext &C) const; - /// Check if the region the expression evaluates to is the standard input, /// and thus, is tainted. static bool isStdin(const Expr *E, CheckerContext &C); @@ -71,16 +75,6 @@ private: /// Given a pointer argument, return the value it points to. static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg); - /// Functions defining the attack surface. - typedef ProgramStateRef (GenericTaintChecker::*FnCheck)( - const CallExpr *, CheckerContext &C) const; - ProgramStateRef postScanf(const CallExpr *CE, CheckerContext &C) const; - ProgramStateRef postSocket(const CallExpr *CE, CheckerContext &C) const; - ProgramStateRef postRetTaint(const CallExpr *CE, CheckerContext &C) const; - - /// Taint the scanned input if the file is tainted. - ProgramStateRef preFscanf(const CallExpr *CE, CheckerContext &C) const; - /// Check for CWE-134: Uncontrolled Format String. static const char MsgUncontrolledFormatString[]; bool checkUncontrolledFormatString(const CallExpr *CE, @@ -103,7 +97,7 @@ private: bool generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const; - typedef SmallVector<unsigned, 2> ArgVector; + using ArgVector = SmallVector<unsigned, 2>; /// A struct used to specify taint propagation rules for a function. /// @@ -115,61 +109,72 @@ private: /// ReturnValueIndex is added to the dst list, the return value will be /// tainted. struct TaintPropagationRule { + enum class VariadicType { None, Src, Dst }; + + using PropagationFuncType = bool (*)(bool IsTainted, const CallExpr *, + CheckerContext &C); + /// List of arguments which can be taint sources and should be checked. ArgVector SrcArgs; /// List of arguments which should be tainted on function return. ArgVector DstArgs; - // TODO: Check if using other data structures would be more optimal. - - TaintPropagationRule() {} - - TaintPropagationRule(unsigned SArg, unsigned DArg, bool TaintRet = false) { - SrcArgs.push_back(SArg); - DstArgs.push_back(DArg); - if (TaintRet) - DstArgs.push_back(ReturnValueIndex); - } - - TaintPropagationRule(unsigned SArg1, unsigned SArg2, unsigned DArg, - bool TaintRet = false) { - SrcArgs.push_back(SArg1); - SrcArgs.push_back(SArg2); - DstArgs.push_back(DArg); - if (TaintRet) - DstArgs.push_back(ReturnValueIndex); - } + /// Index for the first variadic parameter if exist. + unsigned VariadicIndex; + /// Show when a function has variadic parameters. If it has, it marks all + /// of them as source or destination. + VariadicType VarType; + /// Special function for tainted source determination. If defined, it can + /// override the default behavior. + PropagationFuncType PropagationFunc; + + TaintPropagationRule() + : VariadicIndex(InvalidArgIndex), VarType(VariadicType::None), + PropagationFunc(nullptr) {} + + TaintPropagationRule(std::initializer_list<unsigned> &&Src, + std::initializer_list<unsigned> &&Dst, + VariadicType Var = VariadicType::None, + unsigned VarIndex = InvalidArgIndex, + PropagationFuncType Func = nullptr) + : SrcArgs(std::move(Src)), DstArgs(std::move(Dst)), + VariadicIndex(VarIndex), VarType(Var), PropagationFunc(Func) {} /// Get the propagation rule for a given function. static TaintPropagationRule getTaintPropagationRule(const FunctionDecl *FDecl, StringRef Name, CheckerContext &C); - inline void addSrcArg(unsigned A) { SrcArgs.push_back(A); } - inline void addDstArg(unsigned A) { DstArgs.push_back(A); } + void addSrcArg(unsigned A) { SrcArgs.push_back(A); } + void addDstArg(unsigned A) { DstArgs.push_back(A); } - inline bool isNull() const { return SrcArgs.empty(); } + bool isNull() const { + return SrcArgs.empty() && DstArgs.empty() && + VariadicType::None == VarType; + } - inline bool isDestinationArgument(unsigned ArgNum) const { - return (std::find(DstArgs.begin(), DstArgs.end(), ArgNum) != - DstArgs.end()); + bool isDestinationArgument(unsigned ArgNum) const { + return (llvm::find(DstArgs, ArgNum) != DstArgs.end()); } - static inline bool isTaintedOrPointsToTainted(const Expr *E, - ProgramStateRef State, - CheckerContext &C) { - if (State->isTainted(E, C.getLocationContext()) || isStdin(E, C)) + static bool isTaintedOrPointsToTainted(const Expr *E, ProgramStateRef State, + CheckerContext &C) { + if (isTainted(State, E, C.getLocationContext()) || isStdin(E, C)) return true; if (!E->getType().getTypePtr()->isPointerType()) return false; Optional<SVal> V = getPointedToSVal(C, E); - return (V && State->isTainted(*V)); + return (V && isTainted(State, *V)); } /// Pre-process a function which propagates taint according to the /// taint rule. ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const; + + // Functions for custom taintedness propagation. + static bool postSocket(bool IsTainted, const CallExpr *CE, + CheckerContext &C); }; }; @@ -187,8 +192,7 @@ const char GenericTaintChecker::MsgSanitizeSystemArgs[] = const char GenericTaintChecker::MsgTaintedBufferSize[] = "Untrusted data is used to specify the buffer size " "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " - "for " - "character data and the null terminator)"; + "for character data and the null terminator)"; } // end of anonymous namespace @@ -207,24 +211,42 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( // Check for exact name match for functions without builtin substitutes. TaintPropagationRule Rule = llvm::StringSwitch<TaintPropagationRule>(Name) - .Case("atoi", TaintPropagationRule(0, ReturnValueIndex)) - .Case("atol", TaintPropagationRule(0, ReturnValueIndex)) - .Case("atoll", TaintPropagationRule(0, ReturnValueIndex)) - .Case("getc", TaintPropagationRule(0, ReturnValueIndex)) - .Case("fgetc", TaintPropagationRule(0, ReturnValueIndex)) - .Case("getc_unlocked", TaintPropagationRule(0, ReturnValueIndex)) - .Case("getw", TaintPropagationRule(0, ReturnValueIndex)) - .Case("toupper", TaintPropagationRule(0, ReturnValueIndex)) - .Case("tolower", TaintPropagationRule(0, ReturnValueIndex)) - .Case("strchr", TaintPropagationRule(0, ReturnValueIndex)) - .Case("strrchr", TaintPropagationRule(0, ReturnValueIndex)) - .Case("read", TaintPropagationRule(0, 2, 1, true)) - .Case("pread", TaintPropagationRule(InvalidArgIndex, 1, true)) - .Case("gets", TaintPropagationRule(InvalidArgIndex, 0, true)) - .Case("fgets", TaintPropagationRule(2, 0, true)) - .Case("getline", TaintPropagationRule(2, 0)) - .Case("getdelim", TaintPropagationRule(3, 0)) - .Case("fgetln", TaintPropagationRule(0, ReturnValueIndex)) + // Source functions + // TODO: Add support for vfscanf & family. + .Case("fdopen", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("fopen", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("freopen", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("getch", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("getchar", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("getchar_unlocked", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("getenv", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("gets", TaintPropagationRule({}, {0, ReturnValueIndex})) + .Case("scanf", TaintPropagationRule({}, {}, VariadicType::Dst, 1)) + .Case("socket", + TaintPropagationRule({}, {ReturnValueIndex}, VariadicType::None, + InvalidArgIndex, + &TaintPropagationRule::postSocket)) + .Case("wgetch", TaintPropagationRule({}, {ReturnValueIndex})) + // Propagating functions + .Case("atoi", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("atol", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("atoll", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("fgetc", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("fgetln", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("fgets", TaintPropagationRule({2}, {0, ReturnValueIndex})) + .Case("fscanf", TaintPropagationRule({0}, {}, VariadicType::Dst, 2)) + .Case("getc", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("getc_unlocked", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("getdelim", TaintPropagationRule({3}, {0})) + .Case("getline", TaintPropagationRule({2}, {0})) + .Case("getw", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("pread", + TaintPropagationRule({0, 1, 2, 3}, {1, ReturnValueIndex})) + .Case("read", TaintPropagationRule({0, 2}, {1, ReturnValueIndex})) + .Case("strchr", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("strrchr", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("tolower", TaintPropagationRule({0}, {ReturnValueIndex})) + .Case("toupper", TaintPropagationRule({0}, {ReturnValueIndex})) .Default(TaintPropagationRule()); if (!Rule.isNull()) @@ -239,12 +261,12 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( case Builtin::BImemmove: case Builtin::BIstrncpy: case Builtin::BIstrncat: - return TaintPropagationRule(1, 2, 0, true); + return TaintPropagationRule({1, 2}, {0, ReturnValueIndex}); case Builtin::BIstrlcpy: case Builtin::BIstrlcat: - return TaintPropagationRule(1, 2, 0, false); + return TaintPropagationRule({1, 2}, {0}); case Builtin::BIstrndup: - return TaintPropagationRule(0, 1, ReturnValueIndex); + return TaintPropagationRule({0, 1}, {ReturnValueIndex}); default: break; @@ -252,20 +274,23 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( // Process all other functions which could be defined as builtins. if (Rule.isNull()) { - if (C.isCLibraryFunction(FDecl, "snprintf") || - C.isCLibraryFunction(FDecl, "sprintf")) - return TaintPropagationRule(InvalidArgIndex, 0, true); + if (C.isCLibraryFunction(FDecl, "snprintf")) + return TaintPropagationRule({1}, {0, ReturnValueIndex}, VariadicType::Src, + 3); + else if (C.isCLibraryFunction(FDecl, "sprintf")) + return TaintPropagationRule({}, {0, ReturnValueIndex}, VariadicType::Src, + 2); else if (C.isCLibraryFunction(FDecl, "strcpy") || C.isCLibraryFunction(FDecl, "stpcpy") || C.isCLibraryFunction(FDecl, "strcat")) - return TaintPropagationRule(1, 0, true); + return TaintPropagationRule({1}, {0, ReturnValueIndex}); else if (C.isCLibraryFunction(FDecl, "bcopy")) - return TaintPropagationRule(0, 2, 1, false); + return TaintPropagationRule({0, 2}, {1}); else if (C.isCLibraryFunction(FDecl, "strdup") || C.isCLibraryFunction(FDecl, "strdupa")) - return TaintPropagationRule(0, ReturnValueIndex); + return TaintPropagationRule({0}, {ReturnValueIndex}); else if (C.isCLibraryFunction(FDecl, "wcsdup")) - return TaintPropagationRule(0, ReturnValueIndex); + return TaintPropagationRule({0}, {ReturnValueIndex}); } // Skipping the following functions, since they might be used for cleansing @@ -277,19 +302,26 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( void GenericTaintChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - // Check for errors first. + // Check for taintedness related errors first: system call, uncontrolled + // format string, tainted buffer size. if (checkPre(CE, C)) return; - // Add taint second. + // Marks the function's arguments and/or return value tainted if it present in + // the list. addSourcesPre(CE, C); } void GenericTaintChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - if (propagateFromPre(CE, C)) - return; - addSourcesPost(CE, C); + // Set the marked values as tainted. The return value only accessible from + // checkPostStmt. + propagateFromPre(CE, C); +} + +void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + printTaint(State, Out, NL, Sep); } void GenericTaintChecker::addSourcesPre(const CallExpr *CE, @@ -314,13 +346,6 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE, return; } - // Otherwise, check if we have custom pre-processing implemented. - FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) - .Case("fscanf", &GenericTaintChecker::preFscanf) - .Default(nullptr); - // Check and evaluate the call. - if (evalFunction) - State = (this->*evalFunction)(CE, C); if (!State) return; C.addTransition(State); @@ -337,14 +362,10 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, if (TaintArgs.isEmpty()) return false; - for (llvm::ImmutableSet<unsigned>::iterator I = TaintArgs.begin(), - E = TaintArgs.end(); - I != E; ++I) { - unsigned ArgNum = *I; - + for (unsigned ArgNum : TaintArgs) { // Special handling for the tainted return value. if (ArgNum == ReturnValueIndex) { - State = State->addTaint(CE, C.getLocationContext()); + State = addTaint(State, CE, C.getLocationContext()); continue; } @@ -355,7 +376,7 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, const Expr *Arg = CE->getArg(ArgNum); Optional<SVal> V = getPointedToSVal(C, Arg); if (V) - State = State->addTaint(*V); + State = addTaint(State, *V); } // Clear up the taint info from the state. @@ -368,43 +389,6 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, return false; } -void GenericTaintChecker::addSourcesPost(const CallExpr *CE, - CheckerContext &C) const { - // Define the attack surface. - // Set the evaluation function by switching on the callee name. - const FunctionDecl *FDecl = C.getCalleeDecl(CE); - if (!FDecl || FDecl->getKind() != Decl::Function) - return; - - StringRef Name = C.getCalleeName(FDecl); - if (Name.empty()) - return; - FnCheck evalFunction = - llvm::StringSwitch<FnCheck>(Name) - .Case("scanf", &GenericTaintChecker::postScanf) - // TODO: Add support for vfscanf & family. - .Case("getchar", &GenericTaintChecker::postRetTaint) - .Case("getchar_unlocked", &GenericTaintChecker::postRetTaint) - .Case("getenv", &GenericTaintChecker::postRetTaint) - .Case("fopen", &GenericTaintChecker::postRetTaint) - .Case("fdopen", &GenericTaintChecker::postRetTaint) - .Case("freopen", &GenericTaintChecker::postRetTaint) - .Case("getch", &GenericTaintChecker::postRetTaint) - .Case("wgetch", &GenericTaintChecker::postRetTaint) - .Case("socket", &GenericTaintChecker::postSocket) - .Default(nullptr); - - // If the callee isn't defined, it is not of security concern. - // Check and evaluate the call. - ProgramStateRef State = nullptr; - if (evalFunction) - State = (this->*evalFunction)(CE, C); - if (!State) - return; - - C.addTransition(State); -} - bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const { @@ -459,54 +443,31 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, ProgramStateRef State = C.getState(); // Check for taint in arguments. - bool IsTainted = false; - for (ArgVector::const_iterator I = SrcArgs.begin(), E = SrcArgs.end(); I != E; - ++I) { - unsigned ArgNum = *I; - - if (ArgNum == InvalidArgIndex) { - // Check if any of the arguments is tainted, but skip the - // destination arguments. - for (unsigned int i = 0; i < CE->getNumArgs(); ++i) { - if (isDestinationArgument(i)) - continue; - if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C))) - break; - } - break; - } - - if (CE->getNumArgs() < (ArgNum + 1)) + bool IsTainted = true; + for (unsigned ArgNum : SrcArgs) { + if (ArgNum >= CE->getNumArgs()) return State; if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C))) break; } + + // Check for taint in variadic arguments. + if (!IsTainted && VariadicType::Src == VarType) { + // Check if any of the arguments is tainted + for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) { + if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C))) + break; + } + } + + if (PropagationFunc) + IsTainted = PropagationFunc(IsTainted, CE, C); + if (!IsTainted) return State; // Mark the arguments which should be tainted after the function returns. - for (ArgVector::const_iterator I = DstArgs.begin(), E = DstArgs.end(); I != E; - ++I) { - unsigned ArgNum = *I; - - // Should we mark all arguments as tainted? - if (ArgNum == InvalidArgIndex) { - // For all pointer and references that were passed in: - // If they are not pointing to const data, mark data as tainted. - // TODO: So far we are just going one level down; ideally we'd need to - // recurse here. - for (unsigned int i = 0; i < CE->getNumArgs(); ++i) { - const Expr *Arg = CE->getArg(i); - // Process pointer argument. - const Type *ArgTy = Arg->getType().getTypePtr(); - QualType PType = ArgTy->getPointeeType(); - if ((!PType.isNull() && !PType.isConstQualified()) || - (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) - State = State->add<TaintArgsOnPostVisit>(i); - } - continue; - } - + for (unsigned ArgNum : DstArgs) { // Should mark the return value? if (ArgNum == ReturnValueIndex) { State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex); @@ -518,66 +479,38 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, State = State->add<TaintArgsOnPostVisit>(ArgNum); } - return State; -} - -// If argument 0 (file descriptor) is tainted, all arguments except for arg 0 -// and arg 1 should get taint. -ProgramStateRef GenericTaintChecker::preFscanf(const CallExpr *CE, - CheckerContext &C) const { - assert(CE->getNumArgs() >= 2); - ProgramStateRef State = C.getState(); - - // Check is the file descriptor is tainted. - if (State->isTainted(CE->getArg(0), C.getLocationContext()) || - isStdin(CE->getArg(0), C)) { - // All arguments except for the first two should get taint. - for (unsigned int i = 2; i < CE->getNumArgs(); ++i) - State = State->add<TaintArgsOnPostVisit>(i); - return State; + // Mark all variadic arguments tainted if present. + if (VariadicType::Dst == VarType) { + // For all pointer and references that were passed in: + // If they are not pointing to const data, mark data as tainted. + // TODO: So far we are just going one level down; ideally we'd need to + // recurse here. + for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) { + const Expr *Arg = CE->getArg(i); + // Process pointer argument. + const Type *ArgTy = Arg->getType().getTypePtr(); + QualType PType = ArgTy->getPointeeType(); + if ((!PType.isNull() && !PType.isConstQualified()) || + (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) + State = State->add<TaintArgsOnPostVisit>(i); + } } - return nullptr; + return State; } // If argument 0(protocol domain) is network, the return value should get taint. -ProgramStateRef GenericTaintChecker::postSocket(const CallExpr *CE, - CheckerContext &C) const { - ProgramStateRef State = C.getState(); - if (CE->getNumArgs() < 3) - return State; - +bool GenericTaintChecker::TaintPropagationRule::postSocket(bool /*IsTainted*/, + const CallExpr *CE, + CheckerContext &C) { SourceLocation DomLoc = CE->getArg(0)->getExprLoc(); StringRef DomName = C.getMacroNameOrSpelling(DomLoc); // White list the internal communication protocols. if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") || DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36")) - return State; - State = State->addTaint(CE, C.getLocationContext()); - return State; -} - -ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE, - CheckerContext &C) const { - ProgramStateRef State = C.getState(); - if (CE->getNumArgs() < 2) - return State; - - // All arguments except for the very first one should get taint. - for (unsigned int i = 1; i < CE->getNumArgs(); ++i) { - // The arguments are pointer arguments. The data they are pointing at is - // tainted after the call. - const Expr *Arg = CE->getArg(i); - Optional<SVal> V = getPointedToSVal(C, Arg); - if (V) - State = State->addTaint(*V); - } - return State; -} + return false; -ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE, - CheckerContext &C) const { - return C.getState()->addTaint(CE, C.getLocationContext()); + return true; } bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { @@ -603,14 +536,14 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { // This region corresponds to a declaration, find out if it's a global/extern // variable named stdin with the proper type. - if (const VarDecl *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { + if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { D = D->getCanonicalDecl(); - if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) - if (const PointerType *PtrTy = - dyn_cast<PointerType>(D->getType().getTypePtr())) - if (PtrTy->getPointeeType().getCanonicalType() == - C.getASTContext().getFILEType().getCanonicalType()) - return true; + if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) { + const auto *PtrTy = dyn_cast<PointerType>(D->getType().getTypePtr()); + if (PtrTy && PtrTy->getPointeeType().getCanonicalType() == + C.getASTContext().getFILEType().getCanonicalType()) + return true; + } } return false; } @@ -648,9 +581,9 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, ProgramStateRef State = C.getState(); Optional<SVal> PointedToSVal = getPointedToSVal(C, E); SVal TaintedSVal; - if (PointedToSVal && State->isTainted(*PointedToSVal)) + if (PointedToSVal && isTainted(State, *PointedToSVal)) TaintedSVal = *PointedToSVal; - else if (State->isTainted(E, C.getLocationContext())) + else if (isTainted(State, E, C.getLocationContext())) TaintedSVal = C.getSVal(E); else return false; @@ -746,3 +679,7 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, void ento::registerGenericTaintChecker(CheckerManager &mgr) { mgr.registerChecker<GenericTaintChecker>(); } + +bool ento::shouldRegisterGenericTaintChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp index 4c2a229428d9..cc2cfb774227 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -1,9 +1,8 @@ //== IdenticalExprChecker.cpp - Identical expression checker----------------==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -116,7 +115,7 @@ bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) { if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) { if (!CS->body_empty()) { const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin()); - if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*ignoreSideEffects=*/ false)) { + if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*IgnoreSideEffects=*/ false)) { PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC); BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions", categories::LogicError, @@ -513,3 +512,7 @@ public: void ento::registerIdenticalExprChecker(CheckerManager &Mgr) { Mgr.registerChecker<FindIdenticalExprChecker>(); } + +bool ento::shouldRegisterIdenticalExprChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index a4f47d727a8f..e3270f1f7be2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -1,9 +1,8 @@ //=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -310,3 +309,7 @@ void ento::registerInnerPointerChecker(CheckerManager &Mgr) { registerInnerPointerCheckerAux(Mgr); Mgr.registerChecker<InnerPointerChecker>(); } + +bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h index 81c95a4813a6..9642588d6a41 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h @@ -1,9 +1,8 @@ //==--- InterCheckerAPI.h ---------------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file allows introduction of checker dependencies. It contains APIs for @@ -17,9 +16,6 @@ class CheckerManager; namespace ento { -/// Register the checker which evaluates CString API calls. -void registerCStringCheckerBasic(CheckerManager &Mgr); - /// Register the part of MallocChecker connected to InnerPointerChecker. void registerInnerPointerCheckerAux(CheckerManager &Mgr); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp index e719e19d68e9..600458a743ea 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -1,9 +1,8 @@ //===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -134,8 +133,6 @@ public: } }; -typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol; - // Structure to record the symbolic begin and end position of a container struct ContainerData { private: @@ -173,41 +170,21 @@ public: } }; -// Structure fo recording iterator comparisons. We needed to retrieve the -// original comparison expression in assumptions. -struct IteratorComparison { -private: - RegionOrSymbol Left, Right; - bool Equality; - -public: - IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq) - : Left(L), Right(R), Equality(Eq) {} - - RegionOrSymbol getLeft() const { return Left; } - RegionOrSymbol getRight() const { return Right; } - bool isEquality() const { return Equality; } - bool operator==(const IteratorComparison &X) const { - return Left == X.Left && Right == X.Right && Equality == X.Equality; - } - bool operator!=(const IteratorComparison &X) const { - return Left != X.Left || Right != X.Right || Equality != X.Equality; - } - void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); } -}; - class IteratorChecker : public Checker<check::PreCall, check::PostCall, check::PostStmt<MaterializeTemporaryExpr>, check::Bind, - check::LiveSymbols, check::DeadSymbols, - eval::Assume> { + check::LiveSymbols, check::DeadSymbols> { std::unique_ptr<BugType> OutOfRangeBugType; std::unique_ptr<BugType> MismatchedBugType; std::unique_ptr<BugType> InvalidatedBugType; - void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal, - const SVal &RVal, OverloadedOperatorKind Op) const; + void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal, + const SVal &LVal, const SVal &RVal, + OverloadedOperatorKind Op) const; + void processComparison(CheckerContext &C, ProgramStateRef State, + SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, + OverloadedOperatorKind Op) const; void verifyAccess(CheckerContext &C, const SVal &Val) const; void verifyDereference(CheckerContext &C, const SVal &Val) const; void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, @@ -282,8 +259,6 @@ public: CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; - ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, - bool Assumption) const; }; } // namespace @@ -293,9 +268,6 @@ REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData) -REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *, - IteratorComparison) - namespace { bool isIteratorType(const QualType &Type); @@ -325,40 +297,23 @@ bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK); bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg); bool frontModifiable(ProgramStateRef State, const MemRegion *Reg); bool backModifiable(ProgramStateRef State, const MemRegion *Reg); -BinaryOperator::Opcode getOpcode(const SymExpr *SE); -const RegionOrSymbol getRegionOrSymbol(const SVal &Val); -const ProgramStateRef processComparison(ProgramStateRef State, - RegionOrSymbol LVal, - RegionOrSymbol RVal, bool Equal); -const ProgramStateRef saveComparison(ProgramStateRef State, - const SymExpr *Condition, const SVal &LVal, - const SVal &RVal, bool Eq); -const IteratorComparison *loadComparison(ProgramStateRef State, - const SymExpr *Condition); SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont); SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont); ProgramStateRef createContainerBegin(ProgramStateRef State, - const MemRegion *Cont, - const SymbolRef Sym); + const MemRegion *Cont, const Expr *E, + QualType T, const LocationContext *LCtx, + unsigned BlockCount); ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, - const SymbolRef Sym); + const Expr *E, QualType T, + const LocationContext *LCtx, + unsigned BlockCount); const IteratorPosition *getIteratorPosition(ProgramStateRef State, const SVal &Val); -const IteratorPosition *getIteratorPosition(ProgramStateRef State, - RegionOrSymbol RegOrSym); ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, const IteratorPosition &Pos); -ProgramStateRef setIteratorPosition(ProgramStateRef State, - RegionOrSymbol RegOrSym, - const IteratorPosition &Pos); ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); -ProgramStateRef adjustIteratorPosition(ProgramStateRef State, - RegionOrSymbol RegOrSym, - const IteratorPosition &Pos, bool Equal); -ProgramStateRef relateIteratorPositions(ProgramStateRef State, - const IteratorPosition &Pos1, - const IteratorPosition &Pos2, - bool Equal); +ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, + long Scale); ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, const MemRegion *Cont); ProgramStateRef @@ -384,6 +339,8 @@ ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, ProgramStateRef rebaseSymbolInIteratorPositionsIf( ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); +ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, + SymbolRef Sym2, bool Equal); const ContainerData *getContainerData(ProgramStateRef State, const MemRegion *Cont); ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, @@ -399,14 +356,14 @@ bool isZero(ProgramStateRef State, const NonLoc &Val); IteratorChecker::IteratorChecker() { OutOfRangeBugType.reset( - new BugType(this, "Iterator out of range", "Misuse of STL APIs")); - OutOfRangeBugType->setSuppressOnSink(true); + new BugType(this, "Iterator out of range", "Misuse of STL APIs", + /*SuppressOnSink=*/true)); MismatchedBugType.reset( - new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs")); - MismatchedBugType->setSuppressOnSink(true); + new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", + /*SuppressOnSink=*/true)); InvalidatedBugType.reset( - new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); - InvalidatedBugType->setSuppressOnSink(true); + new BugType(this, "Iterator invalidated", "Misuse of STL APIs", + /*SuppressOnSink=*/true)); } void IteratorChecker::checkPreCall(const CallEvent &Call, @@ -449,13 +406,15 @@ void IteratorChecker::checkPreCall(const CallEvent &Call, } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { // Check for out-of-range incrementions and decrementions - if (Call.getNumArgs() >= 1) { + if (Call.getNumArgs() >= 1 && + Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), InstCall->getCXXThisVal(), Call.getArgSVal(0)); } } else { - if (Call.getNumArgs() >= 2) { + if (Call.getNumArgs() >= 2 && + Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), Call.getArgSVal(0), Call.getArgSVal(1)); } @@ -609,78 +568,125 @@ void IteratorChecker::checkPostCall(const CallEvent &Call, const auto Op = Func->getOverloadedOperator(); if (isAssignmentOperator(Op)) { const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call); - if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) { + if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) { handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), Call.getArgSVal(0)); - } else { - handleAssign(C, InstCall->getCXXThisVal()); + return; } + + handleAssign(C, InstCall->getCXXThisVal()); + return; } else if (isSimpleComparisonOperator(Op)) { + const auto *OrigExpr = Call.getOriginExpr(); + if (!OrigExpr) + return; + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { - handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(), - Call.getArgSVal(0), Op); - } else { - handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0), - Call.getArgSVal(1), Op); + handleComparison(C, OrigExpr, Call.getReturnValue(), + InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); + return; } + + handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), + Call.getArgSVal(1), Op); + return; } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { - if (Call.getNumArgs() >= 1) { + if (Call.getNumArgs() >= 1 && + Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), Call.getReturnValue(), InstCall->getCXXThisVal(), Call.getArgSVal(0)); + return; } } else { - if (Call.getNumArgs() >= 2) { + if (Call.getNumArgs() >= 2 && + Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), Call.getReturnValue(), Call.getArgSVal(0), Call.getArgSVal(1)); + return; } } } else if (isIncrementOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), Call.getNumArgs()); - } else { - handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), - Call.getNumArgs()); + return; } + + handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), + Call.getNumArgs()); + return; } else if (isDecrementOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), Call.getNumArgs()); - } else { - handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), - Call.getNumArgs()); + return; } + + handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), + Call.getNumArgs()); + return; } } else { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { if (isAssignCall(Func)) { handleAssign(C, InstCall->getCXXThisVal()); - } else if (isClearCall(Func)) { + return; + } + + if (isClearCall(Func)) { handleClear(C, InstCall->getCXXThisVal()); - } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { + return; + } + + if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { handlePushBack(C, InstCall->getCXXThisVal()); - } else if (isPopBackCall(Func)) { + return; + } + + if (isPopBackCall(Func)) { handlePopBack(C, InstCall->getCXXThisVal()); - } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) { + return; + } + + if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) { handlePushFront(C, InstCall->getCXXThisVal()); - } else if (isPopFrontCall(Func)) { + return; + } + + if (isPopFrontCall(Func)) { handlePopFront(C, InstCall->getCXXThisVal()); - } else if (isInsertCall(Func) || isEmplaceCall(Func)) { + return; + } + + if (isInsertCall(Func) || isEmplaceCall(Func)) { handleInsert(C, Call.getArgSVal(0)); - } else if (isEraseCall(Func)) { + return; + } + + if (isEraseCall(Func)) { if (Call.getNumArgs() == 1) { handleErase(C, Call.getArgSVal(0)); - } else if (Call.getNumArgs() == 2) { + return; + } + + if (Call.getNumArgs() == 2) { handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1)); + return; } - } else if (isEraseAfterCall(Func)) { + } + + if (isEraseAfterCall(Func)) { if (Call.getNumArgs() == 1) { handleEraseAfter(C, Call.getArgSVal(0)); - } else if (Call.getNumArgs() == 2) { + return; + } + + if (Call.getNumArgs() == 2) { handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1)); + return; } } } @@ -700,6 +706,7 @@ void IteratorChecker::checkPostCall(const CallEvent &Call, InstCall->getCXXThisVal()); return; } + if (isEndCall(Func)) { handleEnd(C, OrigExpr, Call.getReturnValue(), InstCall->getCXXThisVal()); @@ -839,77 +846,79 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, } } - auto ComparisonMap = State->get<IteratorComparisonMap>(); - for (const auto Comp : ComparisonMap) { - if (!SR.isLive(Comp.first)) { - State = State->remove<IteratorComparisonMap>(Comp.first); - } - } - C.addTransition(State); } -ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond, - bool Assumption) const { - // Load recorded comparison and transfer iterator state between sides - // according to comparison operator and assumption - const auto *SE = Cond.getAsSymExpr(); - if (!SE) - return State; - - auto Opc = getOpcode(SE); - if (Opc != BO_EQ && Opc != BO_NE) - return State; - - bool Negated = false; - const auto *Comp = loadComparison(State, SE); - if (!Comp) { - // Try negated comparison, which is a SymExpr to 0 integer comparison - const auto *SIE = dyn_cast<SymIntExpr>(SE); - if (!SIE) - return State; - - if (SIE->getRHS() != 0) - return State; +void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE, + const SVal &RetVal, const SVal &LVal, + const SVal &RVal, + OverloadedOperatorKind Op) const { + // Record the operands and the operator of the comparison for the next + // evalAssume, if the result is a symbolic expression. If it is a concrete + // value (only one branch is possible), then transfer the state between + // the operands according to the operator and the result + auto State = C.getState(); + const auto *LPos = getIteratorPosition(State, LVal); + const auto *RPos = getIteratorPosition(State, RVal); + const MemRegion *Cont = nullptr; + if (LPos) { + Cont = LPos->getContainer(); + } else if (RPos) { + Cont = RPos->getContainer(); + } + if (!Cont) + return; - SE = SIE->getLHS(); - Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation - Opc = getOpcode(SE); - if (Opc != BO_EQ && Opc != BO_NE) - return State; + // At least one of the iterators have recorded positions. If one of them has + // not then create a new symbol for the offset. + SymbolRef Sym; + if (!LPos || !RPos) { + auto &SymMgr = C.getSymbolManager(); + Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), + C.getASTContext().LongTy, C.blockCount()); + State = assumeNoOverflow(State, Sym, 4); + } - Comp = loadComparison(State, SE); - if (!Comp) - return State; + if (!LPos) { + State = setIteratorPosition(State, LVal, + IteratorPosition::getPosition(Cont, Sym)); + LPos = getIteratorPosition(State, LVal); + } else if (!RPos) { + State = setIteratorPosition(State, RVal, + IteratorPosition::getPosition(Cont, Sym)); + RPos = getIteratorPosition(State, RVal); } - return processComparison(State, Comp->getLeft(), Comp->getRight(), - (Comp->isEquality() == Assumption) != Negated); + processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); } -void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal, - const SVal &LVal, const SVal &RVal, - OverloadedOperatorKind Op) const { - // Record the operands and the operator of the comparison for the next - // evalAssume, if the result is a symbolic expression. If it is a concrete - // value (only one branch is possible), then transfer the state between - // the operands according to the operator and the result - auto State = C.getState(); - if (const auto *Condition = RetVal.getAsSymbolicExpression()) { - const auto *LPos = getIteratorPosition(State, LVal); - const auto *RPos = getIteratorPosition(State, RVal); - if (!LPos && !RPos) - return; - State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual); - C.addTransition(State); - } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { - if ((State = processComparison( - State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal), - (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) { +void IteratorChecker::processComparison(CheckerContext &C, + ProgramStateRef State, SymbolRef Sym1, + SymbolRef Sym2, const SVal &RetVal, + OverloadedOperatorKind Op) const { + if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { + if ((State = relateSymbols(State, Sym1, Sym2, + (Op == OO_EqualEqual) == + (TruthVal->getValue() != 0)))) { C.addTransition(State); } else { C.generateSink(State, C.getPredecessor()); } + return; + } + + const auto ConditionVal = RetVal.getAs<DefinedSVal>(); + if (!ConditionVal) + return; + + if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { + StateTrue = StateTrue->assume(*ConditionVal, true); + C.addTransition(StateTrue); + } + + if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { + StateFalse = StateFalse->assume(*ConditionVal, false); + C.addTransition(StateFalse); } } @@ -974,47 +983,6 @@ void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal, } } -// This function tells the analyzer's engine that symbols produced by our -// checker, most notably iterator positions, are relatively small. -// A distance between items in the container should not be very large. -// By assuming that it is within around 1/8 of the address space, -// we can help the analyzer perform operations on these symbols -// without being afraid of integer overflows. -// FIXME: Should we provide it as an API, so that all checkers could use it? -static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, - long Scale) { - SValBuilder &SVB = State->getStateManager().getSValBuilder(); - BasicValueFactory &BV = SVB.getBasicValueFactory(); - - QualType T = Sym->getType(); - assert(T->isSignedIntegerOrEnumerationType()); - APSIntType AT = BV.getAPSIntType(T); - - ProgramStateRef NewState = State; - - llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale); - SVal IsCappedFromAbove = - SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym), - nonloc::ConcreteInt(Max), SVB.getConditionType()); - if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) { - NewState = NewState->assume(*DV, true); - if (!NewState) - return State; - } - - llvm::APSInt Min = -Max; - SVal IsCappedFromBelow = - SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym), - nonloc::ConcreteInt(Min), SVB.getConditionType()); - if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) { - NewState = NewState->assume(*DV, true); - if (!NewState) - return State; - } - - return NewState; -} - void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, const SVal &RetVal, @@ -1099,14 +1067,34 @@ void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, // Verify match between a container and the container of an iterator Cont = Cont->getMostDerivedObjectRegion(); + if (const auto *ContSym = Cont->getSymbolicBase()) { + if (isa<SymbolConjured>(ContSym->getSymbol())) + return; + } + auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Iter); - if (Pos && Pos->getContainer() != Cont) { + if (!Pos) + return; + + const auto *IterCont = Pos->getContainer(); + + // Skip symbolic regions based on conjured symbols. Two conjured symbols + // may or may not be the same. For example, the same function can return + // the same or a different container but we get different conjured symbols + // for each call. This may cause false positives so omit them from the check. + if (const auto *ContSym = IterCont->getSymbolicBase()) { + if (isa<SymbolConjured>(ContSym->getSymbol())) + return; + } + + if (IterCont != Cont) { auto *N = C.generateNonFatalErrorNode(State); if (!N) { return; } - reportMismatchedBug("Container accessed using foreign iterator argument.", Iter, Cont, C, N); + reportMismatchedBug("Container accessed using foreign iterator argument.", + Iter, Cont, C, N); } } @@ -1115,8 +1103,31 @@ void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1, // Verify match between the containers of two iterators auto State = C.getState(); const auto *Pos1 = getIteratorPosition(State, Iter1); + if (!Pos1) + return; + + const auto *IterCont1 = Pos1->getContainer(); + + // Skip symbolic regions based on conjured symbols. Two conjured symbols + // may or may not be the same. For example, the same function can return + // the same or a different container but we get different conjured symbols + // for each call. This may cause false positives so omit them from the check. + if (const auto *ContSym = IterCont1->getSymbolicBase()) { + if (isa<SymbolConjured>(ContSym->getSymbol())) + return; + } + const auto *Pos2 = getIteratorPosition(State, Iter2); - if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) { + if (!Pos2) + return; + + const auto *IterCont2 = Pos2->getContainer(); + if (const auto *ContSym = IterCont2->getSymbolicBase()) { + if (isa<SymbolConjured>(ContSym->getSymbol())) + return; + } + + if (IterCont1 != IterCont2) { auto *N = C.generateNonFatalErrorNode(State); if (!N) return; @@ -1138,11 +1149,9 @@ void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE, auto State = C.getState(); auto BeginSym = getContainerBegin(State, ContReg); if (!BeginSym) { - auto &SymMgr = C.getSymbolManager(); - BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(), - C.getASTContext().LongTy, C.blockCount()); - State = assumeNoOverflow(State, BeginSym, 4); - State = createContainerBegin(State, ContReg, BeginSym); + State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy, + C.getLocationContext(), C.blockCount()); + BeginSym = getContainerBegin(State, ContReg); } State = setIteratorPosition(State, RetVal, IteratorPosition::getPosition(ContReg, BeginSym)); @@ -1162,11 +1171,9 @@ void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE, auto State = C.getState(); auto EndSym = getContainerEnd(State, ContReg); if (!EndSym) { - auto &SymMgr = C.getSymbolManager(); - EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(), - C.getASTContext().LongTy, C.blockCount()); - State = assumeNoOverflow(State, EndSym, 4); - State = createContainerEnd(State, ContReg, EndSym); + State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy, + C.getLocationContext(), C.blockCount()); + EndSym = getContainerEnd(State, ContReg); } State = setIteratorPosition(State, RetVal, IteratorPosition::getPosition(ContReg, EndSym)); @@ -1853,23 +1860,6 @@ bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) { OK == OO_MinusEqual; } -BinaryOperator::Opcode getOpcode(const SymExpr *SE) { - if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) { - return BSE->getOpcode(); - } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) { - const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt()); - if (!COE) - return BO_Comma; // Extremal value, neither EQ nor NE - if (COE->getOperator() == OO_EqualEqual) { - return BO_EQ; - } else if (COE->getOperator() == OO_ExclaimEqual) { - return BO_NE; - } - return BO_Comma; // Extremal value, neither EQ nor NE - } - return BO_Comma; // Extremal value, neither EQ nor NE -} - bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) { const auto *CRD = getCXXRecordDecl(State, Reg); if (!CRD) @@ -1930,48 +1920,6 @@ const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); } -const RegionOrSymbol getRegionOrSymbol(const SVal &Val) { - if (const auto Reg = Val.getAsRegion()) { - return Reg; - } else if (const auto Sym = Val.getAsSymbol()) { - return Sym; - } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { - return LCVal->getRegion(); - } - return RegionOrSymbol(); -} - -const ProgramStateRef processComparison(ProgramStateRef State, - RegionOrSymbol LVal, - RegionOrSymbol RVal, bool Equal) { - const auto *LPos = getIteratorPosition(State, LVal); - const auto *RPos = getIteratorPosition(State, RVal); - if (LPos && !RPos) { - State = adjustIteratorPosition(State, RVal, *LPos, Equal); - } else if (!LPos && RPos) { - State = adjustIteratorPosition(State, LVal, *RPos, Equal); - } else if (LPos && RPos) { - State = relateIteratorPositions(State, *LPos, *RPos, Equal); - } - return State; -} - -const ProgramStateRef saveComparison(ProgramStateRef State, - const SymExpr *Condition, const SVal &LVal, - const SVal &RVal, bool Eq) { - const auto Left = getRegionOrSymbol(LVal); - const auto Right = getRegionOrSymbol(RVal); - if (!Left || !Right) - return State; - return State->set<IteratorComparisonMap>(Condition, - IteratorComparison(Left, Right, Eq)); -} - -const IteratorComparison *loadComparison(ProgramStateRef State, - const SymExpr *Condition) { - return State->get<IteratorComparisonMap>(Condition); -} - SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) { const auto *CDataPtr = getContainerData(State, Cont); if (!CDataPtr) @@ -1989,32 +1937,47 @@ SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) { } ProgramStateRef createContainerBegin(ProgramStateRef State, - const MemRegion *Cont, - const SymbolRef Sym) { + const MemRegion *Cont, const Expr *E, + QualType T, const LocationContext *LCtx, + unsigned BlockCount) { // Only create if it does not exist const auto *CDataPtr = getContainerData(State, Cont); + if (CDataPtr && CDataPtr->getBegin()) + return State; + + auto &SymMgr = State->getSymbolManager(); + const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, + "begin"); + State = assumeNoOverflow(State, Sym, 4); + if (CDataPtr) { - if (CDataPtr->getBegin()) { - return State; - } const auto CData = CDataPtr->newBegin(Sym); return setContainerData(State, Cont, CData); } + const auto CData = ContainerData::fromBegin(Sym); return setContainerData(State, Cont, CData); } ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, - const SymbolRef Sym) { + const Expr *E, QualType T, + const LocationContext *LCtx, + unsigned BlockCount) { // Only create if it does not exist const auto *CDataPtr = getContainerData(State, Cont); + if (CDataPtr && CDataPtr->getEnd()) + return State; + + auto &SymMgr = State->getSymbolManager(); + const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, + "end"); + State = assumeNoOverflow(State, Sym, 4); + if (CDataPtr) { - if (CDataPtr->getEnd()) { - return State; - } const auto CData = CDataPtr->newEnd(Sym); return setContainerData(State, Cont, CData); } + const auto CData = ContainerData::fromEnd(Sym); return setContainerData(State, Cont, CData); } @@ -2042,17 +2005,6 @@ const IteratorPosition *getIteratorPosition(ProgramStateRef State, return nullptr; } -const IteratorPosition *getIteratorPosition(ProgramStateRef State, - RegionOrSymbol RegOrSym) { - if (RegOrSym.is<const MemRegion *>()) { - auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion(); - return State->get<IteratorRegionMap>(Reg); - } else if (RegOrSym.is<SymbolRef>()) { - return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>()); - } - return nullptr; -} - ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, const IteratorPosition &Pos) { if (auto Reg = Val.getAsRegion()) { @@ -2066,18 +2018,6 @@ ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, return nullptr; } -ProgramStateRef setIteratorPosition(ProgramStateRef State, - RegionOrSymbol RegOrSym, - const IteratorPosition &Pos) { - if (RegOrSym.is<const MemRegion *>()) { - auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion(); - return State->set<IteratorRegionMap>(Reg, Pos); - } else if (RegOrSym.is<SymbolRef>()) { - return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos); - } - return nullptr; -} - ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { if (auto Reg = Val.getAsRegion()) { Reg = Reg->getMostDerivedObjectRegion(); @@ -2090,21 +2030,8 @@ ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { return nullptr; } -ProgramStateRef adjustIteratorPosition(ProgramStateRef State, - RegionOrSymbol RegOrSym, - const IteratorPosition &Pos, - bool Equal) { - if (Equal) { - return setIteratorPosition(State, RegOrSym, Pos); - } else { - return State; - } -} - -ProgramStateRef relateIteratorPositions(ProgramStateRef State, - const IteratorPosition &Pos1, - const IteratorPosition &Pos2, - bool Equal) { +ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, + SymbolRef Sym2, bool Equal) { auto &SVB = State->getStateManager().getSValBuilder(); // FIXME: This code should be reworked as follows: @@ -2113,14 +2040,16 @@ ProgramStateRef relateIteratorPositions(ProgramStateRef State, // 3. Compare the result to 0. // 4. Assume the result of the comparison. const auto comparison = - SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()), - nonloc::SymbolVal(Pos2.getOffset()), - SVB.getConditionType()); + SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), + nonloc::SymbolVal(Sym2), SVB.getConditionType()); assert(comparison.getAs<DefinedSVal>() && "Symbol comparison must be a `DefinedSVal`"); auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); + if (!NewState) + return nullptr; + if (const auto CompSym = comparison.getAsSymbol()) { assert(isa<SymIntExpr>(CompSym) && "Symbol comparison must be a `SymIntExpr`"); @@ -2161,6 +2090,47 @@ bool isBoundThroughLazyCompoundVal(const Environment &Env, return false; } +// This function tells the analyzer's engine that symbols produced by our +// checker, most notably iterator positions, are relatively small. +// A distance between items in the container should not be very large. +// By assuming that it is within around 1/8 of the address space, +// we can help the analyzer perform operations on these symbols +// without being afraid of integer overflows. +// FIXME: Should we provide it as an API, so that all checkers could use it? +ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, + long Scale) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + BasicValueFactory &BV = SVB.getBasicValueFactory(); + + QualType T = Sym->getType(); + assert(T->isSignedIntegerOrEnumerationType()); + APSIntType AT = BV.getAPSIntType(T); + + ProgramStateRef NewState = State; + + llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale); + SVal IsCappedFromAbove = + SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym), + nonloc::ConcreteInt(Max), SVB.getConditionType()); + if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) { + NewState = NewState->assume(*DV, true); + if (!NewState) + return State; + } + + llvm::APSInt Min = -Max; + SVal IsCappedFromBelow = + SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym), + nonloc::ConcreteInt(Min), SVB.getConditionType()); + if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) { + NewState = NewState->assume(*DV, true); + if (!NewState) + return State; + } + + return NewState; +} + template <typename Condition, typename Process> ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond, Process Proc) { @@ -2379,7 +2349,6 @@ bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc); } - bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, BinaryOperator::Opcode Opc) { auto &SVB = State->getStateManager().getSValBuilder(); @@ -2395,12 +2364,24 @@ bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, } // namespace +void ento::registerIteratorModeling(CheckerManager &mgr) { + mgr.registerChecker<IteratorChecker>(); +} + +bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &Mgr) { \ - auto *checker = Mgr.registerChecker<IteratorChecker>(); \ + auto *checker = Mgr.getChecker<IteratorChecker>(); \ checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \ checker->CheckNames[IteratorChecker::CK_##name] = \ Mgr.getCurrentCheckName(); \ + } \ + \ + bool ento::shouldRegister##name(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(IteratorRangeChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index aade62fd7491..2b75f3acc922 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -1,9 +1,8 @@ //===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -736,12 +735,24 @@ public: }; } // end anonymous namespace +void ento::registerIvarInvalidationModeling(CheckerManager &mgr) { + mgr.registerChecker<IvarInvalidationChecker>(); +} + +bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ IvarInvalidationChecker *checker = \ - mgr.registerChecker<IvarInvalidationChecker>(); \ + mgr.getChecker<IvarInvalidationChecker>(); \ checker->Filter.check_##name = true; \ checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ + } \ + \ + bool ento::shouldRegister##name(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(InstanceVariableInvalidation) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index df238f2b2e45..7522fdd0a99b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -1,9 +1,8 @@ //=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -314,3 +313,7 @@ public: void ento::registerLLVMConventionsChecker(CheckerManager &mgr) { mgr.registerChecker<LLVMConventionsChecker>(); } + +bool ento::shouldRegisterLLVMConventionsChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index eda39efeca17..46067ecbca99 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -1,9 +1,8 @@ //=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -754,10 +753,9 @@ void NonLocalizedStringChecker::reportLocalizationError( if (isDebuggingContext(C)) return; - ExplodedNode *ErrNode = C.getPredecessor(); static CheckerProgramPointTag Tag("NonLocalizedStringChecker", "UnlocalizedString"); - ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag); + ExplodedNode *ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag); if (!ErrNode) return; @@ -1141,7 +1139,7 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( } bool Invalid = false; - llvm::MemoryBuffer *BF = + const llvm::MemoryBuffer *BF = Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid); if (Invalid) return; @@ -1398,14 +1396,27 @@ void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) { NonLocalizedStringChecker *checker = mgr.registerChecker<NonLocalizedStringChecker>(); checker->IsAggressive = - mgr.getAnalyzerOptions().getCheckerBooleanOption("AggressiveReport", - false, checker); + mgr.getAnalyzerOptions().getCheckerBooleanOption( + checker, "AggressiveReport"); +} + +bool ento::shouldRegisterNonLocalizedStringChecker(const LangOptions &LO) { + return true; } void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) { mgr.registerChecker<EmptyLocalizationContextChecker>(); } +bool ento::shouldRegisterEmptyLocalizationContextChecker( + const LangOptions &LO) { + return true; +} + void ento::registerPluralMisuseChecker(CheckerManager &mgr) { mgr.registerChecker<PluralMisuseChecker>(); } + +bool ento::shouldRegisterPluralMisuseChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp new file mode 100644 index 000000000000..6e7776bb484e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -0,0 +1,295 @@ +//== MIGChecker.cpp - MIG calling convention checker ------------*- C++ -*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines MIGChecker, a Mach Interface Generator calling convention +// checker. Namely, in MIG callback implementation the following rules apply: +// - When a server routine returns an error code that represents success, it +// must take ownership of resources passed to it (and eventually release +// them). +// - Additionally, when returning success, all out-parameters must be +// initialized. +// - When it returns any other error code, it must not take ownership, +// because the message and its out-of-line parameters will be destroyed +// by the client that called the function. +// For now we only check the last rule, as its violations lead to dangerous +// use-after-free exploits. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/AnyCall.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>, + check::EndFunction> { + BugType BT{this, "Use-after-free (MIG calling convention violation)", + categories::MemoryError}; + + // The checker knows that an out-of-line object is deallocated if it is + // passed as an argument to one of these functions. If this object is + // additionally an argument of a MIG routine, the checker keeps track of that + // information and issues a warning when an error is returned from the + // respective routine. + std::vector<std::pair<CallDescription, unsigned>> Deallocators = { +#define CALL(required_args, deallocated_arg, ...) \ + {{{__VA_ARGS__}, required_args}, deallocated_arg} + // E.g., if the checker sees a C function 'vm_deallocate' that is + // defined on class 'IOUserClient' that has exactly 3 parameters, it knows + // that argument #1 (starting from 0, i.e. the second argument) is going + // to be consumed in the sense of the MIG consume-on-success convention. + CALL(3, 1, "vm_deallocate"), + CALL(3, 1, "mach_vm_deallocate"), + CALL(2, 0, "mig_deallocate"), + CALL(2, 1, "mach_port_deallocate"), + CALL(1, 0, "device_deallocate"), + CALL(1, 0, "iokit_remove_connect_reference"), + CALL(1, 0, "iokit_remove_reference"), + CALL(1, 0, "iokit_release_port"), + CALL(1, 0, "ipc_port_release"), + CALL(1, 0, "ipc_port_release_sonce"), + CALL(1, 0, "ipc_voucher_attr_control_release"), + CALL(1, 0, "ipc_voucher_release"), + CALL(1, 0, "lock_set_dereference"), + CALL(1, 0, "memory_object_control_deallocate"), + CALL(1, 0, "pset_deallocate"), + CALL(1, 0, "semaphore_dereference"), + CALL(1, 0, "space_deallocate"), + CALL(1, 0, "space_inspect_deallocate"), + CALL(1, 0, "task_deallocate"), + CALL(1, 0, "task_inspect_deallocate"), + CALL(1, 0, "task_name_deallocate"), + CALL(1, 0, "thread_deallocate"), + CALL(1, 0, "thread_inspect_deallocate"), + CALL(1, 0, "upl_deallocate"), + CALL(1, 0, "vm_map_deallocate"), + // E.g., if the checker sees a method 'releaseAsyncReference64()' that is + // defined on class 'IOUserClient' that takes exactly 1 argument, it knows + // that the argument is going to be consumed in the sense of the MIG + // consume-on-success convention. + CALL(1, 0, "IOUserClient", "releaseAsyncReference64"), + CALL(1, 0, "IOUserClient", "releaseNotificationPort"), +#undef CALL + }; + + CallDescription OsRefRetain{"os_ref_retain", 1}; + + void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const; + +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + // HACK: We're making two attempts to find the bug: checkEndFunction + // should normally be enough but it fails when the return value is a literal + // that never gets put into the Environment and ends of function with multiple + // returns get agglutinated across returns, preventing us from obtaining + // the return value. The problem is similar to https://reviews.llvm.org/D25326 + // but now we step into it in the top-level function. + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { + checkReturnAux(RS, C); + } + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const { + checkReturnAux(RS, C); + } + +}; +} // end anonymous namespace + +// A flag that says that the programmer has called a MIG destructor +// for at least one parameter. +REGISTER_TRAIT_WITH_PROGRAMSTATE(ReleasedParameter, bool) +// A set of parameters for which the check is suppressed because +// reference counting is being performed. +REGISTER_SET_WITH_PROGRAMSTATE(RefCountedParameters, const ParmVarDecl *) + +static const ParmVarDecl *getOriginParam(SVal V, CheckerContext &C, + bool IncludeBaseRegions = false) { + // TODO: We should most likely always include base regions here. + SymbolRef Sym = V.getAsSymbol(IncludeBaseRegions); + if (!Sym) + return nullptr; + + // If we optimistically assume that the MIG routine never re-uses the storage + // that was passed to it as arguments when it invalidates it (but at most when + // it assigns to parameter variables directly), this procedure correctly + // determines if the value was loaded from the transitive closure of MIG + // routine arguments in the heap. + while (const MemRegion *MR = Sym->getOriginRegion()) { + const auto *VR = dyn_cast<VarRegion>(MR); + if (VR && VR->hasStackParametersStorage() && + VR->getStackFrame()->inTopFrame()) + return cast<ParmVarDecl>(VR->getDecl()); + + const SymbolicRegion *SR = MR->getSymbolicBase(); + if (!SR) + return nullptr; + + Sym = SR->getSymbol(); + } + + return nullptr; +} + +static bool isInMIGCall(CheckerContext &C) { + const LocationContext *LC = C.getLocationContext(); + assert(LC && "Unknown location context"); + + const StackFrameContext *SFC; + // Find the top frame. + while (LC) { + SFC = LC->getStackFrame(); + LC = SFC->getParent(); + } + + const Decl *D = SFC->getDecl(); + + if (Optional<AnyCall> AC = AnyCall::forDecl(D)) { + // Even though there's a Sema warning when the return type of an annotated + // function is not a kern_return_t, this warning isn't an error, so we need + // an extra sanity check here. + // FIXME: AnyCall doesn't support blocks yet, so they remain unchecked + // for now. + if (!AC->getReturnType(C.getASTContext()) + .getCanonicalType()->isSignedIntegerType()) + return false; + } + + if (D->hasAttr<MIGServerRoutineAttr>()) + return true; + + // See if there's an annotated method in the superclass. + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) + for (const auto *OMD: MD->overridden_methods()) + if (OMD->hasAttr<MIGServerRoutineAttr>()) + return true; + + return false; +} + +void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { + if (Call.isCalled(OsRefRetain)) { + // If the code is doing reference counting over the parameter, + // it opens up an opportunity for safely calling a destructor function. + // TODO: We should still check for over-releases. + if (const ParmVarDecl *PVD = + getOriginParam(Call.getArgSVal(0), C, /*IncludeBaseRegions=*/true)) { + // We never need to clean up the program state because these are + // top-level parameters anyway, so they're always live. + C.addTransition(C.getState()->add<RefCountedParameters>(PVD)); + } + return; + } + + if (!isInMIGCall(C)) + return; + + auto I = llvm::find_if(Deallocators, + [&](const std::pair<CallDescription, unsigned> &Item) { + return Call.isCalled(Item.first); + }); + if (I == Deallocators.end()) + return; + + ProgramStateRef State = C.getState(); + unsigned ArgIdx = I->second; + SVal Arg = Call.getArgSVal(ArgIdx); + const ParmVarDecl *PVD = getOriginParam(Arg, C); + if (!PVD || State->contains<RefCountedParameters>(PVD)) + return; + + const NoteTag *T = C.getNoteTag([this, PVD](BugReport &BR) -> std::string { + if (&BR.getBugType() != &BT) + return ""; + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << "Value passed through parameter '" << PVD->getName() + << "\' is deallocated"; + return OS.str(); + }); + C.addTransition(State->set<ReleasedParameter>(true), T); +} + +// Returns true if V can potentially represent a "successful" kern_return_t. +static bool mayBeSuccess(SVal V, CheckerContext &C) { + ProgramStateRef State = C.getState(); + + // Can V represent KERN_SUCCESS? + if (!State->isNull(V).isConstrainedFalse()) + return true; + + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &ACtx = C.getASTContext(); + + // Can V represent MIG_NO_REPLY? + static const int MigNoReply = -305; + V = SVB.evalEQ(C.getState(), V, SVB.makeIntVal(MigNoReply, ACtx.IntTy)); + if (!State->isNull(V).isConstrainedTrue()) + return true; + + // If none of the above, it's definitely an error. + return false; +} + +void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const { + // It is very unlikely that a MIG callback will be called from anywhere + // within the project under analysis and the caller isn't itself a routine + // that follows the MIG calling convention. Therefore we're safe to believe + // that it's always the top frame that is of interest. There's a slight chance + // that the user would want to enforce the MIG calling convention upon + // a random routine in the middle of nowhere, but given that the convention is + // fairly weird and hard to follow in the first place, there's relatively + // little motivation to spread it this way. + if (!C.inTopFrame()) + return; + + if (!isInMIGCall(C)) + return; + + // We know that the function is non-void, but what if the return statement + // is not there in the code? It's not a compile error, we should not crash. + if (!RS) + return; + + ProgramStateRef State = C.getState(); + if (!State->get<ReleasedParameter>()) + return; + + SVal V = C.getSVal(RS); + if (mayBeSuccess(V, C)) + return; + + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + auto R = llvm::make_unique<BugReport>( + BT, + "MIG callback fails with error after deallocating argument value. " + "This is a use-after-free vulnerability because the caller will try to " + "deallocate it again", + N); + + R->addRange(RS->getSourceRange()); + bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, false); + C.emitReport(std::move(R)); +} + +void ento::registerMIGChecker(CheckerManager &Mgr) { + Mgr.registerChecker<MIGChecker>(); +} + +bool ento::shouldRegisterMIGChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp index fb9bccebe465..b250d3f8795e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -1,9 +1,8 @@ //===-- MPIBugReporter.cpp - bug reporter -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h index 32fcb07e3371..6fbc30288655 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -1,9 +1,8 @@ //===-- MPIBugReporter.h - bug reporter -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp index 28c6898f7947..7f9ba0de1dc2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -1,9 +1,8 @@ //===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -188,3 +187,7 @@ void MPIChecker::allRegionsUsedByWait( void clang::ento::registerMPIChecker(CheckerManager &MGR) { MGR.registerChecker<clang::ento::mpi::MPIChecker>(); } + +bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h index 6b1c062ef3d5..ce9f1afac209 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h @@ -1,9 +1,8 @@ //===-- MPIChecker.h - Verify MPI API usage- --------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp index 12760abaeeff..277b3ed2e105 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp @@ -1,9 +1,8 @@ //===-- MPIFunctionClassifier.cpp - classifies MPI functions ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h index 2e7140cd771e..fe0fb2a4d0e7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h @@ -1,9 +1,8 @@ //===-- MPITypes.h - Functionality to model MPI concepts --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 06e27fc5718d..32ba9bc8e2ef 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -1,9 +1,8 @@ //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This checker flags misuses of KeyChainAPI. In particular, the password data @@ -662,3 +661,7 @@ void MacOSKeychainAPIChecker::printState(raw_ostream &Out, void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { mgr.registerChecker<MacOSKeychainAPIChecker>(); } + +bool ento::shouldRegisterMacOSKeychainAPIChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index f5976d7da4c1..1c52d20d0991 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -1,9 +1,8 @@ // MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -174,3 +173,7 @@ void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, void ento::registerMacOSXAPIChecker(CheckerManager &mgr) { mgr.registerChecker<MacOSXAPIChecker>(); } + +bool ento::shouldRegisterMacOSXAPIChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index ae1b1fc837be..a79b34189065 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1,9 +1,8 @@ //=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -18,6 +17,7 @@ #include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -178,9 +178,10 @@ public: II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), - II_if_nameindex(nullptr), II_if_freenameindex(nullptr), - II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr), - II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), + II_kfree(nullptr), II_if_nameindex(nullptr), + II_if_freenameindex(nullptr), II_wcsdup(nullptr), + II_win_wcsdup(nullptr), II_g_malloc(nullptr), II_g_malloc0(nullptr), + II_g_realloc(nullptr), II_g_try_malloc(nullptr), II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), @@ -250,13 +251,13 @@ private: mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, - *II_if_nameindex, *II_if_freenameindex, *II_wcsdup, - *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, - *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, - *II_g_try_realloc, *II_g_free, *II_g_memdup, - *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n, - *II_g_try_malloc_n, *II_g_try_malloc0_n, - *II_g_try_realloc_n; + *II_kfree, *II_if_nameindex, *II_if_freenameindex, + *II_wcsdup, *II_win_wcsdup, *II_g_malloc, + *II_g_malloc0, *II_g_realloc, *II_g_try_malloc, + *II_g_try_malloc0, *II_g_try_realloc, *II_g_free, + *II_g_memdup, *II_g_malloc_n, *II_g_malloc0_n, + *II_g_realloc_n, *II_g_try_malloc_n, + *II_g_try_malloc0_n, *II_g_try_realloc_n; mutable Optional<uint64_t> KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -359,6 +360,11 @@ private: /// Check if the memory associated with this symbol was released. bool isReleased(SymbolRef Sym, CheckerContext &C) const; + /// See if deallocation happens in a suspicious context. If so, escape the + /// pointers that otherwise would have been deallocated and return true. + bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE, + CheckerContext &C) const; + bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, @@ -599,6 +605,7 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_strndup = &Ctx.Idents.get("strndup"); II_wcsdup = &Ctx.Idents.get("wcsdup"); II_kmalloc = &Ctx.Idents.get("kmalloc"); + II_kfree = &Ctx.Idents.get("kfree"); II_if_nameindex = &Ctx.Idents.get("if_nameindex"); II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); @@ -658,7 +665,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (Family == AF_Malloc && CheckFree) { if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || - FunI == II_g_free) + FunI == II_g_free || FunI == II_kfree) return true; } @@ -875,7 +882,10 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = CallocMem(C, CE, State); State = ProcessZeroAllocation(C, CE, 0, State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_free || FunI == II_g_free) { + } else if (FunI == II_free || FunI == II_g_free || FunI == II_kfree) { + if (suppressDeallocationsInSuspiciousContexts(CE, C)) + return; + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } else if (FunI == II_strdup || FunI == II_win_strdup || FunI == II_wcsdup || FunI == II_win_wcsdup) { @@ -985,7 +995,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation( } else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { if (NE->isArray()) - Arg = NE->getArraySize(); + Arg = *NE->getArraySize(); else return State; } @@ -1117,7 +1127,7 @@ ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C, SVal ElementCount; const SubRegion *Region; if (NE->isArray()) { - const Expr *SizeExpr = NE->getArraySize(); + const Expr *SizeExpr = *NE->getArraySize(); ElementCount = C.getSVal(SizeExpr); // Store the extent size for the (symbolic)region // containing the elements. @@ -1207,7 +1217,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(), /*Hold=*/true, ReleasedAllocatedMemory, - /*RetNullOnFailure=*/true); + /*ReturnsNullOnFailure=*/true); C.addTransition(State); } @@ -2301,14 +2311,14 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, assert(N); if (!BT_Leak[*CheckKind]) { - BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", - categories::MemoryError)); // Leaks should not be reported if they are post-dominated by a sink: // (1) Sinks are higher importance bugs. // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending // with __noreturn functions such as assert() or exit(). We choose not // to report leaks on such paths. - BT_Leak[*CheckKind]->setSuppressOnSink(true); + BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", + categories::MemoryError, + /*SuppressOnSink=*/true)); } // Most bug reports are cached at the location where they occurred. @@ -2531,6 +2541,35 @@ bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { return (RS && RS->isReleased()); } +bool MallocChecker::suppressDeallocationsInSuspiciousContexts( + const CallExpr *CE, CheckerContext &C) const { + if (CE->getNumArgs() == 0) + return false; + + StringRef FunctionStr = ""; + if (const auto *FD = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) + if (const Stmt *Body = FD->getBody()) + if (Body->getBeginLoc().isValid()) + FunctionStr = + Lexer::getSourceText(CharSourceRange::getTokenRange( + {FD->getBeginLoc(), Body->getBeginLoc()}), + C.getSourceManager(), C.getLangOpts()); + + // We do not model the Integer Set Library's retain-count based allocation. + if (!FunctionStr.contains("__isl_")) + return false; + + ProgramStateRef State = C.getState(); + + for (const Expr *Arg : CE->arguments()) + if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol()) + if (const RefState *RS = State->get<RegionState>(Sym)) + State = State->set<RegionState>(Sym, RefState::getEscaped(RS)); + + C.addTransition(State); + return true; +} + bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const { @@ -2832,7 +2871,6 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, if (const RefState *RS = State->get<RegionState>(sym)) { if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && CheckRefState(RS)) { - State = State->remove<RegionState>(sym); State = State->set<RegionState>(sym, RefState::getEscaped(RS)); } } @@ -3087,47 +3125,37 @@ markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) { } // end namespace ento } // end namespace clang -void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { - registerCStringCheckerBasic(mgr); - MallocChecker *checker = mgr.registerChecker<MallocChecker>(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - "Optimistic", false, checker); - checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; - checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = - mgr.getCurrentCheckName(); - // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete - // checker. - if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { - checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; - // FIXME: This does not set the correct name, but without this workaround - // no name will be set at all. - checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = - mgr.getCurrentCheckName(); - } -} - // Intended to be used in InnerPointerChecker to register the part of // MallocChecker connected to it. void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { - registerCStringCheckerBasic(mgr); - MallocChecker *checker = mgr.registerChecker<MallocChecker>(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - "Optimistic", false, checker); - checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; - checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = - mgr.getCurrentCheckName(); + MallocChecker *checker = mgr.getChecker<MallocChecker>(); + checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; + checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = + mgr.getCurrentCheckName(); +} + +void ento::registerDynamicMemoryModeling(CheckerManager &mgr) { + auto *checker = mgr.registerChecker<MallocChecker>(); + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( + checker, "Optimistic"); +} + +bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) { + return true; } #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ - registerCStringCheckerBasic(mgr); \ - MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \ - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \ - "Optimistic", false, checker); \ + MallocChecker *checker = mgr.getChecker<MallocChecker>(); \ checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ + } \ + \ + bool ento::shouldRegister##name(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(MallocChecker) REGISTER_CHECKER(NewDeleteChecker) +REGISTER_CHECKER(NewDeleteLeaksChecker) REGISTER_CHECKER(MismatchedDeallocatorChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index d02ed48bceaa..4fd06f24c5bc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -1,9 +1,8 @@ // MallocOverflowSecurityChecker.cpp - Check for malloc overflows -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -334,7 +333,10 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr); } -void -ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) { +void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) { mgr.registerChecker<MallocOverflowSecurityChecker>(); } + +bool ento::shouldRegisterMallocOverflowSecurityChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index bb245d82bc2b..2eb4d7141e28 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -1,9 +1,8 @@ // MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -250,3 +249,7 @@ public: void ento::registerMallocSizeofChecker(CheckerManager &mgr) { mgr.registerChecker<MallocSizeofChecker>(); } + +bool ento::shouldRegisterMallocSizeofChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp index e3b24f20b0f0..270efede8385 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -1,9 +1,8 @@ // MmapWriteExecChecker.cpp - Check for the prot argument -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -83,8 +82,12 @@ void ento::registerMmapWriteExecChecker(CheckerManager &mgr) { mgr.registerChecker<MmapWriteExecChecker>(); Mwec->ProtExecOv = mgr.getAnalyzerOptions() - .getCheckerIntegerOption("MmapProtExec", 0x04, Mwec); + .getCheckerIntegerOption(Mwec, "MmapProtExec"); Mwec->ProtReadOv = mgr.getAnalyzerOptions() - .getCheckerIntegerOption("MmapProtRead", 0x01, Mwec); + .getCheckerIntegerOption(Mwec, "MmapProtRead"); +} + +bool ento::shouldRegisterMmapWriteExecChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Move.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Move.h new file mode 100644 index 000000000000..10644a8fcb37 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Move.h @@ -0,0 +1,30 @@ +//=== Move.h - Tracking moved-from objects. ------------------------*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines inter-checker API for the use-after-move checker. It allows +// dependent checkers to figure out if an object is in a moved-from state. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { +namespace ento { +namespace move { + +/// Returns true if the object is known to have been recently std::moved. +bool isMovedFrom(ProgramStateRef State, const MemRegion *Region); + +} // namespace move +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index 6efa2dfbe5b4..d8a9af78536a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -1,9 +1,8 @@ // MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ExprCXX.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -187,13 +187,17 @@ private: AggressivenessKind Aggressiveness; public: - void setAggressiveness(StringRef Str) { + void setAggressiveness(StringRef Str, CheckerManager &Mgr) { Aggressiveness = llvm::StringSwitch<AggressivenessKind>(Str) .Case("KnownsOnly", AK_KnownsOnly) .Case("KnownsAndLocals", AK_KnownsAndLocals) .Case("All", AK_All) - .Default(AK_KnownsAndLocals); // A sane default. + .Default(AK_Invalid); + + if (Aggressiveness == AK_Invalid) + Mgr.reportInvalidCheckerOptionValue(this, "WarnOn", + "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value"); }; private: @@ -223,6 +227,18 @@ private: REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) +// Define the inter-checker API. +namespace clang { +namespace ento { +namespace move { +bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) { + const RegionState *RS = State->get<TrackedRegionMap>(Region); + return RS && (RS->isMoved() || RS->isReported()); +} +} // namespace move +} // namespace ento +} // namespace clang + // If a region is removed all of the subregions needs to be removed too. static ProgramStateRef removeFromState(ProgramStateRef State, const MemRegion *Region) { @@ -502,9 +518,9 @@ bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { std::string MethodName = MethodDec->getName().lower(); // TODO: Some of these methods (eg., resize) are not always resetting // the state, so we should consider looking at the arguments. - if (MethodName == "reset" || MethodName == "clear" || - MethodName == "destroy" || MethodName == "resize" || - MethodName == "shrink") + if (MethodName == "assign" || MethodName == "clear" || + MethodName == "destroy" || MethodName == "reset" || + MethodName == "resize" || MethodName == "shrink") return true; } return false; @@ -736,5 +752,9 @@ void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, void ento::registerMoveChecker(CheckerManager &mgr) { MoveChecker *chk = mgr.registerChecker<MoveChecker>(); chk->setAggressiveness( - mgr.getAnalyzerOptions().getCheckerStringOption("WarnOn", "", chk)); + mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr); +} + +bool ento::shouldRegisterMoveChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 4ed1b25cb09e..6fc7c17bc42f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -1,9 +1,8 @@ //=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -76,6 +75,9 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, } void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { - if (mgr.getLangOpts().getGC() != LangOptions::NonGC) - mgr.registerChecker<NSAutoreleasePoolChecker>(); + mgr.registerChecker<NSAutoreleasePoolChecker>(); +} + +bool ento::shouldRegisterNSAutoreleasePoolChecker(const LangOptions &LO) { + return LO.getGC() != LangOptions::NonGC; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 06c43c6b9470..5cec012258c1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -1,9 +1,8 @@ //=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -308,16 +307,30 @@ static bool IsCFError(QualType T, IdentifierInfo *II) { return TT->getDecl()->getIdentifier() == II; } +void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) { + mgr.registerChecker<NSOrCFErrorDerefChecker>(); +} + +bool ento::shouldRegisterNSOrCFErrorDerefChecker(const LangOptions &LO) { + return true; +} + void ento::registerNSErrorChecker(CheckerManager &mgr) { mgr.registerChecker<NSErrorMethodChecker>(); - NSOrCFErrorDerefChecker *checker = - mgr.registerChecker<NSOrCFErrorDerefChecker>(); + NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>(); checker->ShouldCheckNSError = true; } +bool ento::shouldRegisterNSErrorChecker(const LangOptions &LO) { + return true; +} + void ento::registerCFErrorChecker(CheckerManager &mgr) { mgr.registerChecker<CFErrorFunctionChecker>(); - NSOrCFErrorDerefChecker *checker = - mgr.registerChecker<NSOrCFErrorDerefChecker>(); + NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>(); checker->ShouldCheckCFError = true; } + +bool ento::shouldRegisterCFErrorChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index 83d4b5b0758b..fc34255bf6c9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -1,9 +1,8 @@ //=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -143,3 +142,7 @@ void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { mgr.registerChecker<NoReturnFunctionChecker>(); } + +bool ento::shouldRegisterNoReturnFunctionChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 3c4363b6850e..bf6b3e3e87cf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -1,9 +1,8 @@ //===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -217,3 +216,7 @@ std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( void ento::registerNonNullParamChecker(CheckerManager &mgr) { mgr.registerChecker<NonNullParamChecker>(); } + +bool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp index ce9e950aa9ba..43dbe57b8432 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -1,9 +1,8 @@ //==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -107,14 +106,21 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { return true; // Look through the typedefs. - while (auto *T = dyn_cast<TypedefType>(Ty)) { - Ty = T->getDecl()->getUnderlyingType(); - - // It is sufficient for any intermediate typedef - // to be classified const. - HasConst = HasConst || Ty.isConstQualified(); - if (isNonnullType(Ty) && HasConst) - return true; + while (const Type *T = Ty.getTypePtr()) { + if (const auto *TT = dyn_cast<TypedefType>(T)) { + Ty = TT->getDecl()->getUnderlyingType(); + // It is sufficient for any intermediate typedef + // to be classified const. + HasConst = HasConst || Ty.isConstQualified(); + if (isNonnullType(Ty) && HasConst) + return true; + } else if (const auto *AT = dyn_cast<AttributedType>(T)) { + if (AT->getAttrKind() == attr::TypeNonNull) + return true; + Ty = AT->getModifiedType(); + } else { + return false; + } } return false; } @@ -138,3 +144,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { Mgr.registerChecker<NonnullGlobalConstantsChecker>(); } + +bool ento::shouldRegisterNonnullGlobalConstantsChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index e535d1ae27ac..af21c84b995b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -1,9 +1,8 @@ -//== Nullabilityhecker.cpp - Nullability checker ----------------*- C++ -*--==// +//===-- NullabilityChecker.cpp - Nullability checker ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -479,7 +478,7 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { return; const MemRegion *Region = - getTrackRegion(Event.Location, /*CheckSuperregion=*/true); + getTrackRegion(Event.Location, /*CheckSuperRegion=*/true); if (!Region) return; @@ -1192,16 +1191,28 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, } } +void ento::registerNullabilityBase(CheckerManager &mgr) { + mgr.registerChecker<NullabilityChecker>(); +} + +bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name, trackingRequired) \ void ento::register##name##Checker(CheckerManager &mgr) { \ - NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \ + NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ checker->Filter.Check##name = true; \ checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ checker->NeedTracking = checker->NeedTracking || trackingRequired; \ checker->NoDiagnoseCallsToSystemHeaders = \ checker->NoDiagnoseCallsToSystemHeaders || \ - mgr.getAnalyzerOptions().getCheckerBooleanOption( \ - "NoDiagnoseCallsToSystemHeaders", false, checker, true); \ + mgr.getAnalyzerOptions().getCheckerBooleanOption( \ + checker, "NoDiagnoseCallsToSystemHeaders", true); \ + } \ + \ + bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \ + return true; \ } // The checks are likely to be turned on by default and it is possible to do diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp index 4e3a7205f1f4..1053424ae6fa 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp @@ -1,9 +1,8 @@ //===- NumberObjectConversionChecker.cpp -------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -347,5 +346,9 @@ void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) { NumberObjectConversionChecker *Chk = Mgr.registerChecker<NumberObjectConversionChecker>(); Chk->Pedantic = - Mgr.getAnalyzerOptions().getCheckerBooleanOption("Pedantic", false, Chk); + Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic"); +} + +bool ento::shouldRegisterNumberObjectConversionChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp new file mode 100644 index 000000000000..5b9895c338d8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp @@ -0,0 +1,90 @@ +//===- OSObjectCStyleCast.cpp ------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines OSObjectCStyleCast checker, which checks for C-style casts +// of OSObjects. Such casts almost always indicate a code smell, +// as an explicit static or dynamic cast should be used instead. +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/Support/Debug.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +const char *WarnAtNode = "OSObjCast"; + +class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; +}; + +static void emitDiagnostics(const BoundNodes &Nodes, + BugReporter &BR, + AnalysisDeclContext *ADC, + const OSObjectCStyleCastChecker *Checker) { + const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode); + assert(CE); + + std::string Diagnostics; + llvm::raw_string_ostream OS(Diagnostics); + OS << "C-style cast of OSObject. Use OSDynamicCast instead."; + + BR.EmitBasicReport( + ADC->getDecl(), + Checker, + /*Name=*/"OSObject C-Style Cast", + /*BugCategory=*/"Security", + OS.str(), + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC), + CE->getSourceRange()); +} + +static auto hasTypePointingTo(DeclarationMatcher DeclM) + -> decltype(hasType(pointerType())) { + return hasType(pointerType(pointee(hasDeclaration(DeclM)))); +} + +void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, + BugReporter &BR) const { + + AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); + + auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast")))); + + auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase"))); + auto OSObjSubclassM = hasTypePointingTo( + cxxRecordDecl(isDerivedFrom("OSObject"))); + + auto CastM = cStyleCastExpr( + allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))), + OSObjSubclassM)).bind(WarnAtNode); + + auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext()); + for (BoundNodes Match : Matches) + emitDiagnostics(Match, BR, ADC, this); +} +} + +void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) { + Mgr.registerChecker<OSObjectCStyleCastChecker>(); +} + +bool ento::shouldRegisterOSObjectCStyleCast(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 185b57575cb0..bd8cfb14680f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -1,9 +1,8 @@ //== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -89,6 +88,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, } void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { - if (mgr.getLangOpts().ObjC) - mgr.registerChecker<ObjCAtSyncChecker>(); + mgr.registerChecker<ObjCAtSyncChecker>(); +} + +bool ento::shouldRegisterObjCAtSyncChecker(const LangOptions &LO) { + return LO.ObjC; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp index 0424958f8e65..d2371fe60d21 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -1,9 +1,8 @@ //===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -137,7 +136,7 @@ static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR, ADC->getDecl(), Checker, /*Name=*/(llvm::Twine(ActionMsg) + " autoreleasing out parameter inside autorelease pool").str(), - /*Category=*/"Memory", + /*BugCategory=*/"Memory", (llvm::Twine(ActionMsg) + " autoreleasing out parameter " + (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " + "autorelease pool that may exit before " + Name + " returns; consider " @@ -207,3 +206,7 @@ void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D, void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) { Mgr.registerChecker<ObjCAutoreleaseWriteChecker>(); } + +bool ento::shouldRegisterAutoreleaseWriteChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp index 34ce47823d51..4450c464f89d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -1,9 +1,8 @@ //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -172,3 +171,7 @@ public: void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCContainersASTChecker>(); } + +bool ento::shouldRegisterObjCContainersASTChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index 1c8c0d8dedda..f69a3944a56c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -1,9 +1,8 @@ //== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -187,3 +186,7 @@ void ObjCContainersChecker::printState(raw_ostream &OS, ProgramStateRef State, void ento::registerObjCContainersChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCContainersChecker>(); } + +bool ento::shouldRegisterObjCContainersChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index d383302b2790..33e4d2af000d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -1,9 +1,8 @@ //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -222,6 +221,9 @@ void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { Mgr.registerChecker<ObjCSuperCallChecker>(); } +bool ento::shouldRegisterObjCSuperCallChecker(const LangOptions &LO) { + return true; +} /* ToDo list for expanding this check in the future, the list is not exhaustive. diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp index 018d3fcfceb9..9a49200545e3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp @@ -1,9 +1,8 @@ //==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -79,3 +78,7 @@ void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, void ento::registerObjCPropertyChecker(CheckerManager &Mgr) { Mgr.registerChecker<ObjCPropertyChecker>(); } + +bool ento::shouldRegisterObjCPropertyChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index efa804220765..767b7bf4063c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -1,9 +1,8 @@ //== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -437,3 +436,7 @@ static bool isInitMessage(const ObjCMethodCall &Call) { void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCSelfInitChecker>(); } + +bool ento::shouldRegisterObjCSelfInitChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index 9058784dd345..f435f00c08e7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -1,9 +1,8 @@ //===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -282,8 +281,9 @@ SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, //===----------------------------------------------------------------------===// void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) { - const LangOptions &LangOpts = Mgr.getLangOpts(); - if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) - return; Mgr.registerChecker<ObjCSuperDeallocChecker>(); } + +bool ento::shouldRegisterObjCSuperDeallocChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index 7f7b45316087..4b39a97c7e8d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -1,9 +1,8 @@ //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -186,3 +185,7 @@ public: void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCUnusedIvarsChecker>(); } + +bool ento::shouldRegisterObjCUnusedIvarsChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp index 211db392bf71..0aa410de15ff 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -1,9 +1,8 @@ //=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -17,6 +16,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -33,17 +33,14 @@ namespace { class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> { private: mutable std::unique_ptr<BugType> PaddingBug; - mutable int64_t AllowedPad; mutable BugReporter *BR; public: + int64_t AllowedPad; + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, BugReporter &BRArg) const { BR = &BRArg; - AllowedPad = - MGR.getAnalyzerOptions() - .getCheckerIntegerOption("AllowedPad", 24, this); - assert(AllowedPad >= 0 && "AllowedPad option should be non-negative"); // The calls to checkAST* from AnalysisConsumer don't // visit template instantiations or lambda classes. We @@ -277,15 +274,13 @@ public: long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u); CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits); FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr}; - auto CurBegin = Fields.begin(); - auto CurEnd = Fields.end(); // In the typical case, this will find the last element // of the vector. We won't find a middle element unless // we started on a poorly aligned address or have an overly // aligned field. - auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint); - if (Iter != CurBegin) { + auto Iter = llvm::upper_bound(Fields, InsertPoint); + if (Iter != Fields.begin()) { // We found a field that we can layout with the current alignment. --Iter; NewOffset += Iter->Size; @@ -349,5 +344,14 @@ public: } // namespace void ento::registerPaddingChecker(CheckerManager &Mgr) { - Mgr.registerChecker<PaddingChecker>(); + auto *Checker = Mgr.registerChecker<PaddingChecker>(); + Checker->AllowedPad = Mgr.getAnalyzerOptions() + .getCheckerIntegerOption(Checker, "AllowedPad"); + if (Checker->AllowedPad < 0) + Mgr.reportInvalidCheckerOptionValue( + Checker, "AllowedPad", "a non-negative value"); +} + +bool ento::shouldRegisterPaddingChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index de3a16ebc729..03c3f4dd2357 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -1,9 +1,8 @@ //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -343,3 +342,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp, void ento::registerPointerArithChecker(CheckerManager &mgr) { mgr.registerChecker<PointerArithChecker>(); } + +bool ento::shouldRegisterPointerArithChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp new file mode 100644 index 000000000000..307e59b8eebc --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp @@ -0,0 +1,100 @@ +//== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines PointerIterationChecker which checks for non-determinism +// caused due to iteration of unordered containers of pointer elements. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +// ID of a node at which the diagnostic would be emitted. +constexpr llvm::StringLiteral WarnAtNode = "iter"; + +class PointerIterationChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; +}; + +static void emitDiagnostics(const BoundNodes &Match, const Decl *D, + BugReporter &BR, AnalysisManager &AM, + const PointerIterationChecker *Checker) { + auto *ADC = AM.getAnalysisDeclContext(D); + + const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode); + assert(MarkedStmt); + + auto Range = MarkedStmt->getSourceRange(); + auto Location = PathDiagnosticLocation::createBegin(MarkedStmt, + BR.getSourceManager(), + ADC); + std::string Diagnostics; + llvm::raw_string_ostream OS(Diagnostics); + OS << "Iteration of pointer-like elements " + << "can result in non-deterministic ordering"; + + BR.EmitBasicReport(ADC->getDecl(), Checker, + "Iteration of pointer-like elements", "Non-determinism", + OS.str(), Location, Range); +} + +// Assumption: Iteration of ordered containers of pointers is deterministic. + +// TODO: Currently, we only check for std::unordered_set. Other unordered +// containers like std::unordered_map also need to be handled. + +// TODO: Currently, we do not check what the for loop does with the iterated +// pointer values. Not all iterations may cause non-determinism. For example, +// counting or summing up the elements should not be non-deterministic. + +auto matchUnorderedIterWithPointers() -> decltype(decl()) { + + auto UnorderedContainerM = declRefExpr(to(varDecl(hasType( + recordDecl(hasName("std::unordered_set") + ))))); + + auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType()))); + + auto PointerIterM = stmt(cxxForRangeStmt( + hasLoopVariable(PointerTypeM), + hasRangeInit(UnorderedContainerM) + )).bind(WarnAtNode); + + return decl(forEachDescendant(PointerIterM)); +} + +void PointerIterationChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + auto MatcherM = matchUnorderedIterWithPointers(); + + auto Matches = match(MatcherM, *D, AM.getASTContext()); + for (const auto &Match : Matches) + emitDiagnostics(Match, D, BR, AM, this); +} + +} // end of anonymous namespace + +void ento::registerPointerIterationChecker(CheckerManager &Mgr) { + Mgr.registerChecker<PointerIterationChecker>(); +} + +bool ento::shouldRegisterPointerIterationChecker(const LangOptions &LO) { + return LO.CPlusPlus; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp new file mode 100644 index 000000000000..586d9d3af2a6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp @@ -0,0 +1,113 @@ +//== PointerSortingChecker.cpp --------------------------------- -*- C++ -*--=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines PointerSortingChecker which checks for non-determinism +// caused due to sorting containers with pointer-like elements. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +// ID of a node at which the diagnostic would be emitted. +constexpr llvm::StringLiteral WarnAtNode = "sort"; + +class PointerSortingChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; +}; + +static void emitDiagnostics(const BoundNodes &Match, const Decl *D, + BugReporter &BR, AnalysisManager &AM, + const PointerSortingChecker *Checker) { + auto *ADC = AM.getAnalysisDeclContext(D); + + const auto *MarkedStmt = Match.getNodeAs<CallExpr>(WarnAtNode); + assert(MarkedStmt); + + auto Range = MarkedStmt->getSourceRange(); + auto Location = PathDiagnosticLocation::createBegin(MarkedStmt, + BR.getSourceManager(), + ADC); + std::string Diagnostics; + llvm::raw_string_ostream OS(Diagnostics); + OS << "Sorting pointer-like elements " + << "can result in non-deterministic ordering"; + + BR.EmitBasicReport(ADC->getDecl(), Checker, + "Sorting of pointer-like elements", "Non-determinism", + OS.str(), Location, Range); +} + +auto callsName(const char *FunctionName) -> decltype(callee(functionDecl())) { + return callee(functionDecl(hasName(FunctionName))); +} + +// FIXME: Currently we simply check if std::sort is used with pointer-like +// elements. This approach can have a big false positive rate. Using std::sort, +// std::unique and then erase is common technique for deduplicating a container +// (which in some cases might even be quicker than using, let's say std::set). +// In case a container contains arbitrary memory addresses (e.g. multiple +// things give different stuff but might give the same thing multiple times) +// which we don't want to do things with more than once, we might use +// sort-unique-erase and the sort call will emit a report. +auto matchSortWithPointers() -> decltype(decl()) { + // Match any of these function calls. + auto SortFuncM = anyOf( + callsName("std::is_sorted"), + callsName("std::nth_element"), + callsName("std::partial_sort"), + callsName("std::partition"), + callsName("std::sort"), + callsName("std::stable_partition"), + callsName("std::stable_sort") + ); + + // Match only if the container has pointer-type elements. + auto IteratesPointerEltsM = hasArgument(0, + hasType(cxxRecordDecl(has( + fieldDecl(hasType(hasCanonicalType( + pointsTo(hasCanonicalType(pointerType())) + ))) + )))); + + auto PointerSortM = stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM)) + ).bind(WarnAtNode); + + return decl(forEachDescendant(PointerSortM)); +} + +void PointerSortingChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + auto MatcherM = matchSortWithPointers(); + + auto Matches = match(MatcherM, *D, AM.getASTContext()); + for (const auto &Match : Matches) + emitDiagnostics(Match, D, BR, AM, this); +} + +} // end of anonymous namespace + +void ento::registerPointerSortingChecker(CheckerManager &Mgr) { + Mgr.registerChecker<PointerSortingChecker>(); +} + +bool ento::shouldRegisterPointerSortingChecker(const LangOptions &LO) { + return LO.CPlusPlus; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 41490e45f241..c9512f4fc42f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -1,9 +1,8 @@ //=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -73,3 +72,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, void ento::registerPointerSubChecker(CheckerManager &mgr) { mgr.registerChecker<PointerSubChecker>(); } + +bool ento::shouldRegisterPointerSubChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 66cc37278809..33f677e1c258 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -1,9 +1,8 @@ //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -481,3 +480,7 @@ void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper, void ento::registerPthreadLockChecker(CheckerManager &mgr) { mgr.registerChecker<PthreadLockChecker>(); } + +bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 0652af856643..4a3a8dae23a7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -1,9 +1,8 @@ //==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -13,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "RetainCountChecker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; @@ -29,83 +29,20 @@ const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->get<RefBindings>(Sym); } -ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + +static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, RefVal Val) { assert(Sym != nullptr); return State->set<RefBindings>(Sym, Val); } -ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { +static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->remove<RefBindings>(Sym); } -class UseAfterRelease : public RefCountBug { -public: - UseAfterRelease(const CheckerBase *checker) - : RefCountBug(checker, "Use-after-release") {} - - const char *getDescription() const override { - return "Reference-counted object is used after it is released"; - } -}; - -class BadRelease : public RefCountBug { -public: - BadRelease(const CheckerBase *checker) : RefCountBug(checker, "Bad release") {} - - const char *getDescription() const override { - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - } -}; - -class DeallocNotOwned : public RefCountBug { -public: - DeallocNotOwned(const CheckerBase *checker) - : RefCountBug(checker, "-dealloc sent to non-exclusively owned object") {} - - const char *getDescription() const override { - return "-dealloc sent to object that may be referenced elsewhere"; - } -}; - -class OverAutorelease : public RefCountBug { -public: - OverAutorelease(const CheckerBase *checker) - : RefCountBug(checker, "Object autoreleased too many times") {} - - const char *getDescription() const override { - return "Object autoreleased too many times"; - } -}; - -class ReturnedNotOwnedForOwned : public RefCountBug { -public: - ReturnedNotOwnedForOwned(const CheckerBase *checker) - : RefCountBug(checker, "Method should return an owned object") {} - - const char *getDescription() const override { - return "Object with a +0 retain count returned to caller where a +1 " - "(owning) retain count is expected"; - } -}; - -class Leak : public RefCountBug { -public: - Leak(const CheckerBase *checker, StringRef name) : RefCountBug(checker, name) { - // Leaks should not be reported if they are post-dominated by a sink. - setSuppressOnSink(true); - } - - const char *getDescription() const override { return ""; } - - bool isLeak() const override { return true; } -}; - -} // end namespace retaincountchecker -} // end namespace ento -} // end namespace clang - void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) Out << "Tracked " << T.getAsString() << " | "; @@ -196,7 +133,7 @@ public: ProgramStateRef getState() const { return state; } bool VisitSymbol(SymbolRef sym) override { - state = state->remove<RefBindings>(sym); + state = removeRefBinding(state, sym); return true; } }; @@ -248,7 +185,15 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, if (!BE) return; - ArgEffect AE = ArgEffect(IncRef, ObjKind::ObjC); + QualType QT = CE->getType(); + ObjKind K; + if (QT->isObjCObjectPointerType()) { + K = ObjKind::ObjC; + } else { + K = ObjKind::CF; + } + + ArgEffect AE = ArgEffect(IncRef, K); switch (BE->getBridgeKind()) { case OBC_Bridge: @@ -390,6 +335,31 @@ void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, C.addTransition(State); } +static bool isReceiverUnconsumedSelf(const CallEvent &Call) { + if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) { + + // Check if the message is not consumed, we know it will not be used in + // an assignment, ex: "self = [super init]". + return MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper() && + !Call.getLocationContext() + ->getAnalysisDeclContext() + ->getParentMap() + .isConsumedExpr(Call.getOriginExpr()); + } + return false; +} + +const static RetainSummary *getSummary(RetainSummaryManager &Summaries, + const CallEvent &Call, + QualType ReceiverType) { + const Expr *CE = Call.getOriginExpr(); + AnyCall C = + CE ? *AnyCall::forExpr(CE) + : AnyCall(cast<CXXDestructorDecl>(Call.getDecl())); + return Summaries.getSummary(C, Call.hasNonZeroCallbackArg(), + isReceiverUnconsumedSelf(Call), ReceiverType); +} + void RetainCountChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { RetainSummaryManager &Summaries = getSummaryManager(C); @@ -405,7 +375,7 @@ void RetainCountChecker::checkPostCall(const CallEvent &Call, } } - const RetainSummary *Summ = Summaries.getSummary(Call, ReceiverType); + const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType); if (C.wasInlined) { processSummaryOfInlined(*Summ, Call, C); @@ -414,20 +384,6 @@ void RetainCountChecker::checkPostCall(const CallEvent &Call, checkSummary(*Summ, Call, C); } -RefCountBug * -RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const { - if (!leakWithinFunction) - leakWithinFunction.reset(new Leak(this, "Leak")); - return leakWithinFunction.get(); -} - -RefCountBug * -RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const { - if (!leakAtReturn) - leakAtReturn.reset(new Leak(this, "Leak of returned object")); - return leakAtReturn.get(); -} - /// GetReturnType - Used to get the return type of a message expression or /// function call with the intention of affixing that type to a tracked symbol. /// While the return type can be queried directly from RetEx, when @@ -529,12 +485,36 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, C.addTransition(state); } +static bool isSmartPtrField(const MemRegion *MR) { + const auto *TR = dyn_cast<TypedValueRegion>( + cast<SubRegion>(MR)->getSuperRegion()); + return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType()); +} + + +/// A value escapes in these possible cases: +/// +/// - binding to something that is not a memory region. +/// - binding to a memregion that does not have stack storage +/// - binding to a variable that has a destructor attached using CleanupAttr +/// +/// We do not currently model what happens when a symbol is +/// assigned to a struct field, unless it is a known smart pointer +/// implementation, about which we know that it is inlined. +/// FIXME: This could definitely be improved upon. static bool shouldEscapeRegion(const MemRegion *R) { + if (isSmartPtrField(R)) + return false; + + const auto *VR = dyn_cast<VarRegion>(R); - // We do not currently model what happens when a symbol is - // assigned to a struct field, so be conservative here and let the symbol - // go. TODO: This could definitely be improved upon. - return !R->hasStackStorage() || !isa<VarRegion>(R); + if (!R->hasStackStorage() || !VR) + return true; + + const VarDecl *VD = VR->getDecl(); + if (!VD->hasAttr<CleanupAttr>()) + return false; // CleanupAttr attaches destructors, which cause escaping. + return true; } static SmallVector<ProgramStateRef, 2> @@ -557,6 +537,11 @@ updateOutParameters(ProgramStateRef State, const RetainSummary &Summ, ProgramStateRef AssumeZeroReturn = State; if (SplitNecessary) { + if (!CE.getResultType()->isScalarType()) { + // Structures cannot be assumed. This probably deserves + // a compiler warning for invalid annotations. + return {State}; + } if (auto DL = L.getAs<DefinedOrUnknownSVal>()) { AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true); AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false); @@ -629,7 +614,6 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, // Helper tag for providing diagnostics: indicate whether dealloc was sent // at this location. - static CheckerProgramPointTag DeallocSentTag(this, DeallocTagDescription); bool DeallocSent = false; for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { @@ -855,6 +839,23 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, return setRefBinding(state, sym, V); } +const RefCountBug & +RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind, + SymbolRef Sym) const { + switch (ErrorKind) { + case RefVal::ErrorUseAfterRelease: + return useAfterRelease; + case RefVal::ErrorReleaseNotOwned: + return releaseNotOwned; + case RefVal::ErrorDeallocNotOwned: + if (Sym->getType()->getPointeeCXXRecordDecl()) + return freeNotOwned; + return deallocNotOwned; + default: + llvm_unreachable("Unhandled error."); + } +} + void RetainCountChecker::processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, @@ -874,30 +875,9 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St, if (!N) return; - RefCountBug *BT; - switch (ErrorKind) { - default: - llvm_unreachable("Unhandled error."); - case RefVal::ErrorUseAfterRelease: - if (!useAfterRelease) - useAfterRelease.reset(new UseAfterRelease(this)); - BT = useAfterRelease.get(); - break; - case RefVal::ErrorReleaseNotOwned: - if (!releaseNotOwned) - releaseNotOwned.reset(new BadRelease(this)); - BT = releaseNotOwned.get(); - break; - case RefVal::ErrorDeallocNotOwned: - if (!deallocNotOwned) - deallocNotOwned.reset(new DeallocNotOwned(this)); - BT = deallocNotOwned.get(); - break; - } - - assert(BT); auto report = llvm::make_unique<RefCountReport>( - *BT, C.getASTContext().getLangOpts(), N, Sym); + errorKindToBugKind(ErrorKind, Sym), + C.getASTContext().getLangOpts(), N, Sym); report->addRange(ErrorRange); C.emitReport(std::move(report)); } @@ -906,15 +886,19 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St, // Handle the return values of retain-count-related functions. //===----------------------------------------------------------------------===// -bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - // Get the callee. We're only interested in simple C functions. +bool RetainCountChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { ProgramStateRef state = C.getState(); - const FunctionDecl *FD = C.getCalleeDecl(CE); + const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD) return false; + const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return false; + RetainSummaryManager &SmrMgr = getSummaryManager(C); - QualType ResultTy = CE->getCallReturnType(C.getASTContext()); + QualType ResultTy = Call.getResultType(); // See if the function has 'rc_ownership_trusted_implementation' // annotate attribute. If it does, we will not inline it. @@ -932,18 +916,27 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Bind the return value. if (BSmr == BehaviorSummary::Identity || - BSmr == BehaviorSummary::IdentityOrZero) { - SVal RetVal = state->getSVal(CE->getArg(0), LCtx); + BSmr == BehaviorSummary::IdentityOrZero || + BSmr == BehaviorSummary::IdentityThis) { + + const Expr *BindReturnTo = + (BSmr == BehaviorSummary::IdentityThis) + ? cast<CXXMemberCallExpr>(CE)->getImplicitObjectArgument() + : CE->getArg(0); + SVal RetVal = state->getSVal(BindReturnTo, LCtx); // If the receiver is unknown or the function has // 'rc_ownership_trusted_implementation' annotate attribute, conjure a // return value. + // FIXME: this branch is very strange. if (RetVal.isUnknown() || (hasTrustedImplementationAnnotation && !ResultTy.isNull())) { SValBuilder &SVB = C.getSValBuilder(); RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount()); } + + // Bind the value. state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false); if (BSmr == BehaviorSummary::IdentityOrZero) { @@ -953,13 +946,12 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Assume that output is zero on the other branch. NullOutputState = NullOutputState->BindExpr( CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); - - C.addTransition(NullOutputState); + C.addTransition(NullOutputState, &CastFailTag); // And on the original branch assume that both input and // output are non-zero. if (auto L = RetVal.getAs<DefinedOrUnknownSVal>()) - state = state->assume(*L, /*Assumption=*/true); + state = state->assume(*L, /*assumption=*/true); } } @@ -988,8 +980,10 @@ ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S, return Pred; ProgramStateRef state = C.getState(); - SymbolRef Sym = - state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol(); + // We need to dig down to the symbolic base here because various + // custom allocators do sometimes return the symbol with an offset. + SymbolRef Sym = state->getSValAsScalarOrLoc(RetE, C.getLocationContext()) + .getAsLocSymbol(/*IncludeBaseRegions=*/true); if (!Sym) return Pred; @@ -1057,11 +1051,11 @@ ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S, // FIXME: What is the convention for blocks? Is there one? if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) { - const RetainSummary *Summ = Summaries.getMethodSummary(MD); + const RetainSummary *Summ = Summaries.getSummary(AnyCall(MD)); RE = Summ->getRetEffect(); } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { if (!isa<CXXMethodDecl>(FD)) { - const RetainSummary *Summ = Summaries.getFunctionSummary(FD); + const RetainSummary *Summ = Summaries.getSummary(AnyCall(FD)); RE = Summ->getRetEffect(); } } @@ -1100,8 +1094,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefLeakReport>( - *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C); + auto R = + llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1125,11 +1119,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { - if (!returnNotOwnedForOwned) - returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this)); - auto R = llvm::make_unique<RefCountReport>( - *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1145,39 +1136,15 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const { - // Are we storing to something that causes the value to "escape"? - bool escapes = true; - - // A value escapes in three possible cases (this may change): - // - // (1) we are binding to something that is not a memory region. - // (2) we are binding to a memregion that does not have stack storage ProgramStateRef state = C.getState(); + const MemRegion *MR = loc.getAsRegion(); - if (auto regionLoc = loc.getAs<loc::MemRegionVal>()) { - escapes = shouldEscapeRegion(regionLoc->getRegion()); - } - - // If we are storing the value into an auto function scope variable annotated - // with (__attribute__((cleanup))), stop tracking the value to avoid leak - // false positives. - if (const auto *LVR = dyn_cast_or_null<VarRegion>(loc.getAsRegion())) { - const VarDecl *VD = LVR->getDecl(); - if (VD->hasAttr<CleanupAttr>()) { - escapes = true; - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return; - - // Otherwise, find all symbols referenced by 'val' that we are tracking + // Find all symbols referenced by 'val' that we are tracking // and stop tracking them. - state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); - C.addTransition(state); + if (MR && shouldEscapeRegion(MR)) { + state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); + C.addTransition(state); + } } ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, @@ -1196,14 +1163,14 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, bool changed = false; RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>(); + ConstraintManager &CMgr = state->getConstraintManager(); - for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + for (auto &I : B) { // Check if the symbol is null stop tracking the symbol. - ConstraintManager &CMgr = state->getConstraintManager(); - ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); + ConditionTruthVal AllocFailed = CMgr.isNull(state, I.first); if (AllocFailed.isConstrainedTrue()) { changed = true; - B = RefBFactory.remove(B, I.getKey()); + B = RefBFactory.remove(B, I.first); } } @@ -1213,25 +1180,21 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, return state; } -ProgramStateRef -RetainCountChecker::checkRegionChanges(ProgramStateRef state, - const InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const LocationContext *LCtx, - const CallEvent *Call) const { +ProgramStateRef RetainCountChecker::checkRegionChanges( + ProgramStateRef state, const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, + const CallEvent *Call) const { if (!invalidated) return state; llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; - for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), - E = ExplicitRegions.end(); I != E; ++I) { - if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>()) + + for (const MemRegion *I : ExplicitRegions) + if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>()) WhitelistedSymbols.insert(SR->getSymbol()); - } - for (SymbolRef sym : - llvm::make_range(invalidated->begin(), invalidated->end())) { + for (SymbolRef sym : *invalidated) { if (WhitelistedSymbols.count(sym)) continue; // Remove any existing reference-count binding. @@ -1309,12 +1272,9 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, os << "but "; os << "has a +" << V.getCount() << " retain count"; - if (!overAutorelease) - overAutorelease.reset(new OverAutorelease(this)); - const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefCountReport>(*overAutorelease, LOpts, N, Sym, - os.str()); + auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym, + os.str()); Ctx.emitReport(std::move(R)); } @@ -1356,56 +1316,48 @@ RetainCountChecker::processLeaks(ProgramStateRef state, ExplodedNode *Pred) const { // Generate an intermediate node representing the leak point. ExplodedNode *N = Ctx.addTransition(state, Pred); + const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); if (N) { - for (SmallVectorImpl<SymbolRef>::iterator - I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { - - const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts) - : getLeakAtReturnBug(LOpts); - assert(BT && "BugType not initialized."); - - Ctx.emitReport( - llvm::make_unique<RefLeakReport>(*BT, LOpts, N, *I, Ctx)); + for (SymbolRef L : Leaked) { + const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; + Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx)); } } return N; } -static bool isISLObjectRef(QualType Ty) { - return StringRef(Ty.getAsString()).startswith("isl_"); -} - void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const { if (!Ctx.inTopFrame()) return; RetainSummaryManager &SmrMgr = getSummaryManager(Ctx); const LocationContext *LCtx = Ctx.getLocationContext(); - const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl()); + const Decl *D = LCtx->getDecl(); + Optional<AnyCall> C = AnyCall::forDecl(D); - if (!FD || SmrMgr.isTrustedReferenceCountImplementation(FD)) + if (!C || SmrMgr.isTrustedReferenceCountImplementation(D)) return; ProgramStateRef state = Ctx.getState(); - const RetainSummary *FunctionSummary = SmrMgr.getFunctionSummary(FD); + const RetainSummary *FunctionSummary = SmrMgr.getSummary(*C); ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects(); - for (unsigned idx = 0, e = FD->getNumParams(); idx != e; ++idx) { - const ParmVarDecl *Param = FD->getParamDecl(idx); + for (unsigned idx = 0, e = C->param_size(); idx != e; ++idx) { + const ParmVarDecl *Param = C->parameters()[idx]; SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol(); QualType Ty = Param->getType(); const ArgEffect *AE = CalleeSideArgEffects.lookup(idx); - if (AE && AE->getKind() == DecRef && isISLObjectRef(Ty)) { - state = setRefBinding( - state, Sym, RefVal::makeOwned(ObjKind::Generalized, Ty)); - } else if (isISLObjectRef(Ty)) { - state = setRefBinding( - state, Sym, - RefVal::makeNotOwned(ObjKind::Generalized, Ty)); + if (AE) { + ObjKind K = AE->getObjKind(); + if (K == ObjKind::Generalized || K == ObjKind::OS || + (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) { + RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(K, Ty) + : RefVal::makeNotOwned(K, Ty); + state = setRefBinding(state, Sym, NewVal); + } } } @@ -1431,9 +1383,9 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS, return; } - for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + for (auto &I : B) { state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx, - I->first, I->second); + I.first, I.second); if (!state) return; } @@ -1448,8 +1400,8 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS, B = state->get<RefBindings>(); SmallVector<SymbolRef, 10> Leaked; - for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) - state = handleSymbolDeath(state, I->first, I->second, Leaked); + for (auto &I : B) + state = handleSymbolDeath(state, I.first, I.second, Leaked); processLeaks(state, Leaked, Ctx, Pred); } @@ -1459,7 +1411,6 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, ExplodedNode *Pred = C.getPredecessor(); ProgramStateRef state = C.getState(); - RefBindingsTy B = state->get<RefBindings>(); SmallVector<SymbolRef, 10> Leaked; // Update counts from autorelease pools @@ -1492,12 +1443,10 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Now generate a new node that nukes the old bindings. // The only bindings left at this point are the leaked symbols. RefBindingsTy::Factory &F = state->get_context<RefBindings>(); - B = state->get<RefBindings>(); + RefBindingsTy B = state->get<RefBindings>(); - for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(), - E = Leaked.end(); - I != E; ++I) - B = F.remove(B, *I); + for (SymbolRef L : Leaked) + B = F.remove(B, L); state = state->set<RefBindings>(B); C.addTransition(state, Pred); @@ -1513,9 +1462,9 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, Out << Sep << NL; - for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { - Out << I->first << " : "; - I->second.print(Out); + for (auto &I : B) { + Out << I.first << " : "; + I.second.print(Out); Out << NL; } } @@ -1524,24 +1473,48 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, // Checker registration. //===----------------------------------------------------------------------===// -void ento::registerRetainCountChecker(CheckerManager &Mgr) { - auto *Chk = Mgr.registerChecker<RetainCountChecker>(); - Chk->TrackObjCAndCFObjects = true; +void ento::registerRetainCountBase(CheckerManager &Mgr) { + Mgr.registerChecker<RetainCountChecker>(); +} + +bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) { + return true; } // FIXME: remove this, hack for backwards compatibility: // it should be possible to enable the NS/CF retain count checker as // osx.cocoa.RetainCount, and it should be possible to disable // osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false. -static bool hasPrevCheckOSObjectOptionDisabled(AnalyzerOptions &Options) { - auto I = Options.Config.find("osx.cocoa.RetainCount:CheckOSObject"); +static bool getOption(AnalyzerOptions &Options, + StringRef Postfix, + StringRef Value) { + auto I = Options.Config.find( + (StringRef("osx.cocoa.RetainCount:") + Postfix).str()); if (I != Options.Config.end()) - return I->getValue() == "false"; + return I->getValue() == Value; return false; } +void ento::registerRetainCountChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.getChecker<RetainCountChecker>(); + Chk->TrackObjCAndCFObjects = true; + Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(), + "TrackNSCFStartParam", + "true"); +} + +bool ento::shouldRegisterRetainCountChecker(const LangOptions &LO) { + return true; +} + void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { - auto *Chk = Mgr.registerChecker<RetainCountChecker>(); - if (!hasPrevCheckOSObjectOptionDisabled(Mgr.getAnalyzerOptions())) + auto *Chk = Mgr.getChecker<RetainCountChecker>(); + if (!getOption(Mgr.getAnalyzerOptions(), + "CheckOSObject", + "false")) Chk->TrackOSObjects = true; } + +bool ento::shouldRegisterOSObjectRetainCountChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index 31e2d9ae4932..124c0e5040b9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -1,9 +1,8 @@ //==--- RetainCountChecker.h - Checks for leaks and other issues -*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -22,6 +21,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Analysis/RetainSummaryManager.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Analysis/SelectorExtras.h" @@ -33,7 +33,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" @@ -251,14 +250,21 @@ class RetainCountChecker check::RegionChanges, eval::Assume, eval::Call > { - mutable std::unique_ptr<RefCountBug> useAfterRelease, releaseNotOwned; - mutable std::unique_ptr<RefCountBug> deallocNotOwned; - mutable std::unique_ptr<RefCountBug> overAutorelease, returnNotOwnedForOwned; - mutable std::unique_ptr<RefCountBug> leakWithinFunction, leakAtReturn; + + RefCountBug useAfterRelease{this, RefCountBug::UseAfterRelease}; + RefCountBug releaseNotOwned{this, RefCountBug::ReleaseNotOwned}; + RefCountBug deallocNotOwned{this, RefCountBug::DeallocNotOwned}; + RefCountBug freeNotOwned{this, RefCountBug::FreeNotOwned}; + RefCountBug overAutorelease{this, RefCountBug::OverAutorelease}; + RefCountBug returnNotOwnedForOwned{this, RefCountBug::ReturnNotOwnedForOwned}; + RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction}; + RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn}; + + CheckerProgramPointTag DeallocSentTag{this, "DeallocSent"}; + CheckerProgramPointTag CastFailTag{this, "DynamicCastFail"}; mutable std::unique_ptr<RetainSummaryManager> Summaries; public: - static constexpr const char *DeallocTagDescription = "DeallocSent"; /// Track Objective-C and CoreFoundation objects. bool TrackObjCAndCFObjects = false; @@ -266,22 +272,15 @@ public: /// Track sublcasses of OSObject. bool TrackOSObjects = false; - RetainCountChecker() {} + /// Track initial parameters (for the entry point) for NS/CF objects. + bool TrackNSCFStartParam = false; - RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const; - - RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const; + RetainCountChecker() {}; RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const { - // FIXME: We don't support ARC being turned on and off during one analysis. - // (nor, for that matter, do we support changing ASTContexts) - bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; - if (!Summaries) { - Summaries.reset(new RetainSummaryManager( - Ctx, ARCEnabled, TrackObjCAndCFObjects, TrackOSObjects)); - } else { - assert(Summaries->isARCEnabled() == ARCEnabled); - } + if (!Summaries) + Summaries.reset( + new RetainSummaryManager(Ctx, TrackObjCAndCFObjects, TrackOSObjects)); return *Summaries; } @@ -311,7 +310,7 @@ public: const CallEvent &Call, CheckerContext &C) const; - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const; @@ -336,6 +335,9 @@ public: RefVal V, ArgEffect E, RefVal::Kind &hasErr, CheckerContext &C) const; + const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind, + SymbolRef Sym) const; + void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; @@ -358,6 +360,14 @@ public: CheckerContext &Ctx, ExplodedNode *Pred = nullptr) const; + const CheckerProgramPointTag &getDeallocSentTag() const { + return DeallocSentTag; + } + + const CheckerProgramPointTag &getCastFailTag() const { + return CastFailTag; + } + private: /// Perform the necessary checks and state adjustments at the end of the /// function. @@ -371,11 +381,6 @@ private: const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym); -ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, - RefVal Val); - -ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym); - /// Returns true if this stack frame is for an Objective-C method that is a /// property getter or setter whose body has been synthesized by the analyzer. inline bool isSynthesizedAccessor(const StackFrameContext *SFC) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index cda1a928de13..796fd882ffd5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -1,9 +1,8 @@ // RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -19,6 +18,56 @@ using namespace clang; using namespace ento; using namespace retaincountchecker; +StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { + switch (BT) { + case UseAfterRelease: + return "Use-after-release"; + case ReleaseNotOwned: + return "Bad release"; + case DeallocNotOwned: + return "-dealloc sent to non-exclusively owned object"; + case FreeNotOwned: + return "freeing non-exclusively owned object"; + case OverAutorelease: + return "Object autoreleased too many times"; + case ReturnNotOwnedForOwned: + return "Method should return an owned object"; + case LeakWithinFunction: + return "Leak"; + case LeakAtReturn: + return "Leak of returned object"; + } + llvm_unreachable("Unknown RefCountBugType"); +} + +StringRef RefCountBug::getDescription() const { + switch (BT) { + case UseAfterRelease: + return "Reference-counted object is used after it is released"; + case ReleaseNotOwned: + return "Incorrect decrement of the reference count of an object that is " + "not owned at this point by the caller"; + case DeallocNotOwned: + return "-dealloc sent to object that may be referenced elsewhere"; + case FreeNotOwned: + return "'free' called on an object that may be referenced elsewhere"; + case OverAutorelease: + return "Object autoreleased too many times"; + case ReturnNotOwnedForOwned: + return "Object with a +0 retain count returned to caller where a +1 " + "(owning) retain count is expected"; + case LeakWithinFunction: + case LeakAtReturn: + return ""; + } + llvm_unreachable("Unknown RefCountBugType"); +} + +RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) + : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, + /*SuppressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), + BT(BT), Checker(Checker) {} + static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. return isa<IntegerLiteral>(E) || @@ -42,7 +91,8 @@ static std::string getPrettyTypeName(QualType QT) { /// Write information about the type state change to {@code os}, /// return whether the note should be generated. static bool shouldGenerateNote(llvm::raw_string_ostream &os, - const RefVal *PrevT, const RefVal &CurrV, + const RefVal *PrevT, + const RefVal &CurrV, bool DeallocSent) { // Get the previous type state. RefVal PrevV = *PrevT; @@ -132,6 +182,31 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, return None; } +static Optional<std::string> findMetaClassAlloc(const Expr *Callee) { + if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { + if (ME->getMemberDecl()->getNameAsString() != "alloc") + return None; + const Expr *This = ME->getBase()->IgnoreParenImpCasts(); + if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { + const ValueDecl *VD = DRE->getDecl(); + if (VD->getNameAsString() != "metaClass") + return None; + + if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) + return RD->getNameAsString(); + + } + } + return None; +} + +static std::string findAllocatedObjectName(const Stmt *S, QualType QT) { + if (const auto *CE = dyn_cast<CallExpr>(S)) + if (auto Out = findMetaClassAlloc(CE->getCallee())) + return *Out; + return getPrettyTypeName(QT); +} + static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, @@ -189,7 +264,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, os << "a Core Foundation object of type '" << Sym->getType().getAsString() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { - os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) + os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { os << "an object of type '" << Sym->getType().getAsString() @@ -338,15 +413,38 @@ annotateConsumedSummaryMismatch(const ExplodedNode *N, if (os.str().empty()) return nullptr; - // FIXME: remove the code duplication with NoStoreFuncVisitor. - PathDiagnosticLocation L; - if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { - L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext()); + PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM); + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +} + +/// Annotate the parameter at the analysis entry point. +static std::shared_ptr<PathDiagnosticEventPiece> +annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, + const SourceManager &SM) { + auto PP = N->getLocationAs<BlockEdge>(); + if (!PP) + return nullptr; + + const CFGBlock *Src = PP->getSrc(); + const RefVal *CurrT = getRefBinding(N->getState(), Sym); + + if (&Src->getParent()->getEntry() != Src || !CurrT || + getRefBinding(N->getFirstPred()->getState(), Sym)) + return nullptr; + + const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion()); + const auto *PVD = cast<ParmVarDecl>(VR->getDecl()); + PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM); + + std::string s; + llvm::raw_string_ostream os(s); + os << "Parameter '" << PVD->getNameAsString() << "' starts at +"; + if (CurrT->getCount() == 1) { + os << "1, as it is marked as consuming"; } else { - L = PathDiagnosticLocation( - Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); + assert(CurrT->getCount() == 0); + os << "0"; } - return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } @@ -354,12 +452,22 @@ std::shared_ptr<PathDiagnosticPiece> RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { + const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); + const auto *Checker = + static_cast<const RetainCountChecker *>(BT.getChecker()); + + bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || + BT.getBugType() == RefCountBug::DeallocNotOwned; + const SourceManager &SM = BRC.getSourceManager(); CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); if (auto CE = N->getLocationAs<CallExitBegin>()) if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) return PD; + if (auto PD = annotateStartParameter(N, Sym, SM)) + return PD; + // FIXME: We will eventually need to handle non-statement-based events // (__attribute__((cleanup))). if (!N->getLocation().getAs<StmtPoint>()) @@ -372,7 +480,8 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, const LocationContext *LCtx = N->getLocationContext(); const RefVal* CurrT = getRefBinding(CurrSt, Sym); - if (!CurrT) return nullptr; + if (!CurrT) + return nullptr; const RefVal &CurrV = *CurrT; const RefVal *PrevT = getRefBinding(PrevSt, Sym); @@ -382,6 +491,12 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, std::string sbuf; llvm::raw_string_ostream os(sbuf); + if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { + os << "Object is now not exclusively owned"; + auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); + return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); + } + // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { @@ -428,9 +543,13 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, // program point bool DeallocSent = false; - if (N->getLocation().getTag() && - N->getLocation().getTag()->getTagDescription().contains( - RetainCountChecker::DeallocTagDescription)) { + const ProgramPointTag *Tag = N->getLocation().getTag(); + + if (Tag == &Checker->getCastFailTag()) { + os << "Assuming dynamic cast returns null due to type mismatch"; + } + + if (Tag == &Checker->getDeallocSentTag()) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); @@ -587,7 +706,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, if (AllocationNodeInCurrentOrParentContext && AllocationNodeInCurrentOrParentContext->getLocationContext() != - LeakContext) + LeakContext) FirstBinding = nullptr; return AllocationInfo(AllocationNodeInCurrentOrParentContext, @@ -671,10 +790,19 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, } } else { const FunctionDecl *FD = cast<FunctionDecl>(D); - os << "whose name ('" << *FD - << "') does not contain 'Copy' or 'Create'. This violates the naming" - " convention rules given in the Memory Management Guide for Core" - " Foundation"; + ObjKind K = RV->getObjKind(); + if (K == ObjKind::ObjC || K == ObjKind::CF) { + os << "whose name ('" << *FD + << "') does not contain 'Copy' or 'Create'. This violates the " + "naming" + " convention rules given in the Memory Management Guide for " + "Core" + " Foundation"; + } else if (RV->getObjKind() == ObjKind::OS) { + std::string FuncName = FD->getNameAsString(); + os << "whose name ('" << FuncName + << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'"; + } } } } else { @@ -685,15 +813,15 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } -RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, +RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, - bool registerVisitor) - : BugReport(D, D.getDescription(), n), Sym(sym) { - if (registerVisitor) + bool isLeak) + : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) { + if (!isLeak) addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); } -RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, +RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText) : BugReport(D, D.getDescription(), endText, n) { @@ -779,10 +907,10 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) { } } -RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts, +RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx) - : RefCountReport(D, LOpts, n, sym, false) { + : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) { deriveAllocLocation(Ctx, sym); if (!AllocBinding) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h index 9f796abe8eae..ef3c75f87af5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -1,9 +1,8 @@ //== RetainCountDiagnostics.h - Checks for leaks and other issues -*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -15,42 +14,61 @@ #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H +#include "clang/Analysis/RetainSummaryManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" namespace clang { namespace ento { namespace retaincountchecker { class RefCountBug : public BugType { -protected: - RefCountBug(const CheckerBase *checker, StringRef name) - : BugType(checker, name, categories::MemoryRefCount) {} - public: - virtual const char *getDescription() const = 0; + enum RefCountBugType { + UseAfterRelease, + ReleaseNotOwned, + DeallocNotOwned, + FreeNotOwned, + OverAutorelease, + ReturnNotOwnedForOwned, + LeakWithinFunction, + LeakAtReturn, + }; + RefCountBug(const CheckerBase *checker, RefCountBugType BT); + StringRef getDescription() const; + + RefCountBugType getBugType() const { + return BT; + } + + const CheckerBase *getChecker() const { + return Checker; + } - virtual bool isLeak() const { return false; } +private: + RefCountBugType BT; + const CheckerBase *Checker; + static StringRef bugTypeToName(RefCountBugType BT); }; class RefCountReport : public BugReport { protected: SymbolRef Sym; + bool isLeak = false; public: - RefCountReport(RefCountBug &D, const LangOptions &LOpts, + RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, - bool registerVisitor = true); + bool isLeak=false); - RefCountReport(RefCountBug &D, const LangOptions &LOpts, + RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText); llvm::iterator_range<ranges_iterator> getRanges() override { - const RefCountBug& BugTy = static_cast<RefCountBug&>(getBugType()); - if (!BugTy.isLeak()) + if (!isLeak) return BugReport::getRanges(); return llvm::make_range(ranges_iterator(), ranges_iterator()); } @@ -69,7 +87,7 @@ class RefLeakReport : public RefCountReport { void createDescription(CheckerContext &Ctx); public: - RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, + RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx); PathDiagnosticLocation getLocation(const SourceManager &SM) const override { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 17ef39531628..9eb47e0526dc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -1,9 +1,8 @@ //== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -90,3 +89,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) { mgr.registerChecker<ReturnPointerRangeChecker>(); } + +bool ento::shouldRegisterReturnPointerRangeChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 3e0613e8ba68..f55c369da67e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -1,9 +1,8 @@ //== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -121,3 +120,7 @@ void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE, void ento::registerReturnUndefChecker(CheckerManager &mgr) { mgr.registerChecker<ReturnUndefChecker>(); } + +bool ento::shouldRegisterReturnUndefChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp new file mode 100644 index 000000000000..103208d8b5a5 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -0,0 +1,170 @@ +//===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines ReturnValueChecker, which checks for calls with guaranteed +// boolean return value. It ensures the return value of each function call. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> { +public: + // It sets the predefined invariant ('CDM') if the current call not break it. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + // It reports whether a predefined invariant ('CDM') is broken. + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + +private: + // The pairs are in the following form: {{{class, call}}, return value} + const CallDescriptionMap<bool> CDM = { + // These are known in the LLVM project: 'Error()' + {{{"ARMAsmParser", "Error"}}, true}, + {{{"HexagonAsmParser", "Error"}}, true}, + {{{"LLLexer", "Error"}}, true}, + {{{"LLParser", "Error"}}, true}, + {{{"MCAsmParser", "Error"}}, true}, + {{{"MCAsmParserExtension", "Error"}}, true}, + {{{"TGParser", "Error"}}, true}, + {{{"X86AsmParser", "Error"}}, true}, + // 'TokError()' + {{{"LLParser", "TokError"}}, true}, + {{{"MCAsmParser", "TokError"}}, true}, + {{{"MCAsmParserExtension", "TokError"}}, true}, + {{{"TGParser", "TokError"}}, true}, + // 'error()' + {{{"MIParser", "error"}}, true}, + {{{"WasmAsmParser", "error"}}, true}, + {{{"WebAssemblyAsmParser", "error"}}, true}, + // Other + {{{"AsmParser", "printError"}}, true}}; +}; +} // namespace + +static std::string getName(const CallEvent &Call) { + std::string Name = ""; + if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl())) + if (const CXXRecordDecl *RD = MD->getParent()) + Name += RD->getNameAsString() + "::"; + + Name += Call.getCalleeIdentifier()->getName(); + return Name; +} + +// The predefinitions ('CDM') could break due to the ever growing code base. +// Check for the expected invariants and see whether they apply. +static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, + CheckerContext &C) { + auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); + if (!ReturnDV) + return None; + + if (ExpectedValue) + return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); + + return C.getState()->isNull(*ReturnDV).isConstrainedFalse(); +} + +void ReturnValueChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const bool *RawExpectedValue = CDM.lookup(Call); + if (!RawExpectedValue) + return; + + SVal ReturnV = Call.getReturnValue(); + bool ExpectedValue = *RawExpectedValue; + Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + if (!IsInvariantBreak) + return; + + // If the invariant is broken it is reported by 'checkEndFunction()'. + if (*IsInvariantBreak) + return; + + std::string Name = getName(Call); + const NoteTag *CallTag = C.getNoteTag( + [Name, ExpectedValue](BugReport &) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + Out << '\'' << Name << "' returns " + << (ExpectedValue ? "true" : "false"); + return Out.str(); + }, + /*IsPrunable=*/true); + + ProgramStateRef State = C.getState(); + State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue); + C.addTransition(State, CallTag); +} + +void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &C) const { + if (!RS || !RS->getRetValue()) + return; + + // We cannot get the caller in the top-frame. + const StackFrameContext *SFC = C.getStackFrame(); + if (C.getStackFrame()->inTopFrame()) + return; + + ProgramStateRef State = C.getState(); + CallEventManager &CMgr = C.getStateManager().getCallEventManager(); + CallEventRef<> Call = CMgr.getCaller(SFC, State); + if (!Call) + return; + + const bool *RawExpectedValue = CDM.lookup(*Call); + if (!RawExpectedValue) + return; + + SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); + bool ExpectedValue = *RawExpectedValue; + Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + if (!IsInvariantBreak) + return; + + // If the invariant is appropriate it is reported by 'checkPostCall()'. + if (!*IsInvariantBreak) + return; + + std::string Name = getName(*Call); + const NoteTag *CallTag = C.getNoteTag( + [Name, ExpectedValue](BugReport &BR) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + // The following is swapped because the invariant is broken. + Out << '\'' << Name << "' returns " + << (ExpectedValue ? "false" : "true"); + + return Out.str(); + }, + /*IsPrunable=*/false); + + C.addTransition(State, CallTag); +} + +void ento::registerReturnValueChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ReturnValueChecker>(); +} + +bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp index cf03b3c21132..5e305aa709b6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp @@ -1,9 +1,8 @@ //=- RunLoopAutoreleaseLeakChecker.cpp --------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // //===----------------------------------------------------------------------===// @@ -116,7 +115,7 @@ static void emitDiagnostics(BoundNodes &Match, BR.EmitBasicReport(ADC->getDecl(), Checker, /*Name=*/"Memory leak inside autorelease pool", - /*Category=*/"Memory", + /*BugCategory=*/"Memory", /*Name=*/ (Twine("Temporary objects allocated in the") + " autorelease pool " + @@ -203,3 +202,7 @@ void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D, void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) { mgr.registerChecker<RunLoopAutoreleaseLeakChecker>(); } + +bool ento::shouldRegisterRunLoopAutoreleaseLeakChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 819d437e6883..ec5e9622c236 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -1,9 +1,8 @@ //===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -109,10 +108,10 @@ SimpleStreamChecker::SimpleStreamChecker() DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); - LeakBugType.reset( - new BugType(this, "Resource Leak", "Unix Stream API Error")); // Sinks are higher importance bugs as well as calls to assert() or exit(0). - LeakBugType->setSuppressOnSink(true); + LeakBugType.reset( + new BugType(this, "Resource Leak", "Unix Stream API Error", + /*SuppressOnSink=*/true)); } void SimpleStreamChecker::checkPostCall(const CallEvent &Call, @@ -269,3 +268,8 @@ SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, void ento::registerSimpleStreamChecker(CheckerManager &mgr) { mgr.registerChecker<SimpleStreamChecker>(); } + +// This checker should be enabled regardless of how language options are set. +bool ento::shouldRegisterSimpleStreamChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp new file mode 100644 index 000000000000..fd372aafa50d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -0,0 +1,72 @@ +// SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a checker that models various aspects of +// C++ smart pointer behavior. +// +//===----------------------------------------------------------------------===// + +#include "Move.h" + +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class SmartPtrModeling : public Checker<eval::Call> { + bool isNullAfterMoveMethod(const CallEvent &Call) const; + +public: + bool evalCall(const CallEvent &Call, CheckerContext &C) const; +}; +} // end of anonymous namespace + +bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { + // TODO: Update CallDescription to support anonymous calls? + // TODO: Handle other methods, such as .get() or .release(). + // But once we do, we'd need a visitor to explain null dereferences + // that are found via such modeling. + const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); + return CD && CD->getConversionType()->isBooleanType(); +} + +bool SmartPtrModeling::evalCall(const CallEvent &Call, + CheckerContext &C) const { + if (!isNullAfterMoveMethod(Call)) + return false; + + ProgramStateRef State = C.getState(); + const MemRegion *ThisR = + cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + + if (!move::isMovedFrom(State, ThisR)) { + // TODO: Model this case as well. At least, avoid invalidation of globals. + return false; + } + + // TODO: Add a note to bug reports describing this decision. + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + return true; +} + +void ento::registerSmartPtrModeling(CheckerManager &Mgr) { + Mgr.registerChecker<SmartPtrModeling>(); +} + +bool ento::shouldRegisterSmartPtrModeling(const LangOptions &LO) { + return LO.CPlusPlus; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 0f53d826a5f6..b93bed5c3097 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -1,9 +1,8 @@ //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -360,11 +359,23 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, } } -#define REGISTER_CHECKER(name) \ - void ento::register##name(CheckerManager &Mgr) { \ - StackAddrEscapeChecker *Chk = \ - Mgr.registerChecker<StackAddrEscapeChecker>(); \ - Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \ +void ento::registerStackAddrEscapeBase(CheckerManager &mgr) { + mgr.registerChecker<StackAddrEscapeChecker>(); +} + +bool ento::shouldRegisterStackAddrEscapeBase(const LangOptions &LO) { + return true; +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &Mgr) { \ + StackAddrEscapeChecker *Chk = \ + Mgr.getChecker<StackAddrEscapeChecker>(); \ + Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \ + } \ + \ + bool ento::shouldRegister##name(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(StackAddrEscapeChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 6478128ce954..2cdee8da375e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -1,9 +1,8 @@ //=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -225,7 +224,7 @@ class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> { public: void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; private: Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD, @@ -368,12 +367,16 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, } } -bool StdLibraryFunctionsChecker::evalCall(const CallExpr *CE, +bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { - const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl()); + const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD) return false; + const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return false; + Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C); if (!FoundSummary) return false; @@ -1056,3 +1059,7 @@ void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { // class, turning on different function summaries. mgr.registerChecker<StdLibraryFunctionsChecker>(); } + +bool ento::shouldRegisterStdCLibraryFunctionsChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 92647f032730..1ea5e0769513 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1,9 +1,8 @@ //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -15,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -72,7 +72,7 @@ public: II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr), II_ferror(nullptr), II_fileno(nullptr) {} - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; private: @@ -104,11 +104,15 @@ private: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) -bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const FunctionDecl *FD = C.getCalleeDecl(CE); +bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { + const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD || FD->getKind() != Decl::Function) return false; + const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return false; + ASTContext &Ctx = C.getASTContext(); if (!II_fopen) II_fopen = &Ctx.Idents.get("fopen"); @@ -409,3 +413,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, void ento::registerStreamChecker(CheckerManager &mgr) { mgr.registerChecker<StreamChecker>(); } + +bool ento::shouldRegisterStreamChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Taint.cpp new file mode 100644 index 000000000000..bc120949ee4f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Taint.cpp @@ -0,0 +1,227 @@ +//=== Taint.cpp - Taint tracking and basic propagation rules. ------*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines basic, non-domain-specific mechanisms for tracking tainted values. +// +//===----------------------------------------------------------------------===// + +#include "Taint.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; +using namespace taint; + +// Fully tainted symbols. +REGISTER_MAP_WITH_PROGRAMSTATE(TaintMap, SymbolRef, TaintTagType) + +// Partially tainted symbols. +REGISTER_MAP_FACTORY_WITH_PROGRAMSTATE(TaintedSubRegions, const SubRegion *, + TaintTagType) +REGISTER_MAP_WITH_PROGRAMSTATE(DerivedSymTaint, SymbolRef, TaintedSubRegions) + +void taint::printTaint(ProgramStateRef State, raw_ostream &Out, const char *NL, + const char *Sep) { + TaintMapTy TM = State->get<TaintMap>(); + + if (!TM.isEmpty()) + Out << "Tainted symbols:" << NL; + + for (const auto &I : TM) + Out << I.first << " : " << I.second << NL; +} + +void dumpTaint(ProgramStateRef State) { + printTaint(State, llvm::errs()); +} + +ProgramStateRef taint::addTaint(ProgramStateRef State, const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind) { + return addTaint(State, State->getSVal(S, LCtx), Kind); +} + +ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V, + TaintTagType Kind) { + SymbolRef Sym = V.getAsSymbol(); + if (Sym) + return addTaint(State, Sym, Kind); + + // If the SVal represents a structure, try to mass-taint all values within the + // structure. For now it only works efficiently on lazy compound values that + // were conjured during a conservative evaluation of a function - either as + // return values of functions that return structures or arrays by value, or as + // values of structures or arrays passed into the function by reference, + // directly or through pointer aliasing. Such lazy compound values are + // characterized by having exactly one binding in their captured store within + // their parent region, which is a conjured symbol default-bound to the base + // region of the parent region. + if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { + if (Optional<SVal> binding = + State->getStateManager().getStoreManager() + .getDefaultBinding(*LCV)) { + if (SymbolRef Sym = binding->getAsSymbol()) + return addPartialTaint(State, Sym, LCV->getRegion(), Kind); + } + } + + const MemRegion *R = V.getAsRegion(); + return addTaint(State, R, Kind); +} + +ProgramStateRef taint::addTaint(ProgramStateRef State, const MemRegion *R, + TaintTagType Kind) { + if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) + return addTaint(State, SR->getSymbol(), Kind); + return State; +} + +ProgramStateRef taint::addTaint(ProgramStateRef State, SymbolRef Sym, + TaintTagType Kind) { + // If this is a symbol cast, remove the cast before adding the taint. Taint + // is cast agnostic. + while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) + Sym = SC->getOperand(); + + ProgramStateRef NewState = State->set<TaintMap>(Sym, Kind); + assert(NewState); + return NewState; +} + +ProgramStateRef taint::addPartialTaint(ProgramStateRef State, + SymbolRef ParentSym, + const SubRegion *SubRegion, + TaintTagType Kind) { + // Ignore partial taint if the entire parent symbol is already tainted. + if (const TaintTagType *T = State->get<TaintMap>(ParentSym)) + if (*T == Kind) + return State; + + // Partial taint applies if only a portion of the symbol is tainted. + if (SubRegion == SubRegion->getBaseRegion()) + return addTaint(State, ParentSym, Kind); + + const TaintedSubRegions *SavedRegs = State->get<DerivedSymTaint>(ParentSym); + TaintedSubRegions::Factory &F = State->get_context<TaintedSubRegions>(); + TaintedSubRegions Regs = SavedRegs ? *SavedRegs : F.getEmptyMap(); + + Regs = F.add(Regs, SubRegion, Kind); + ProgramStateRef NewState = State->set<DerivedSymTaint>(ParentSym, Regs); + assert(NewState); + return NewState; +} + +bool taint::isTainted(ProgramStateRef State, const Stmt *S, + const LocationContext *LCtx, TaintTagType Kind) { + SVal val = State->getSVal(S, LCtx); + return isTainted(State, val, Kind); +} + +bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) { + if (const SymExpr *Sym = V.getAsSymExpr()) + return isTainted(State, Sym, Kind); + if (const MemRegion *Reg = V.getAsRegion()) + return isTainted(State, Reg, Kind); + return false; +} + +bool taint::isTainted(ProgramStateRef State, const MemRegion *Reg, + TaintTagType K) { + if (!Reg) + return false; + + // Element region (array element) is tainted if either the base or the offset + // are tainted. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) + return isTainted(State, ER->getSuperRegion(), K) || + isTainted(State, ER->getIndex(), K); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) + return isTainted(State, SR->getSymbol(), K); + + if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) + return isTainted(State, ER->getSuperRegion(), K); + + return false; +} + +bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) { + if (!Sym) + return false; + + // Traverse all the symbols this symbol depends on to see if any are tainted. + for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), + SE = Sym->symbol_end(); SI != SE; ++SI) { + if (!isa<SymbolData>(*SI)) + continue; + + if (const TaintTagType *Tag = State->get<TaintMap>(*SI)) { + if (*Tag == Kind) + return true; + } + + if (const auto *SD = dyn_cast<SymbolDerived>(*SI)) { + // If this is a SymbolDerived with a tainted parent, it's also tainted. + if (isTainted(State, SD->getParentSymbol(), Kind)) + return true; + + // If this is a SymbolDerived with the same parent symbol as another + // tainted SymbolDerived and a region that's a sub-region of that tainted + // symbol, it's also tainted. + if (const TaintedSubRegions *Regs = + State->get<DerivedSymTaint>(SD->getParentSymbol())) { + const TypedValueRegion *R = SD->getRegion(); + for (auto I : *Regs) { + // FIXME: The logic to identify tainted regions could be more + // complete. For example, this would not currently identify + // overlapping fields in a union as tainted. To identify this we can + // check for overlapping/nested byte offsets. + if (Kind == I.second && R->isSubRegionOf(I.first)) + return true; + } + } + } + + // If memory region is tainted, data is also tainted. + if (const auto *SRV = dyn_cast<SymbolRegionValue>(*SI)) { + if (isTainted(State, SRV->getRegion(), Kind)) + return true; + } + + // If this is a SymbolCast from a tainted value, it's also tainted. + if (const auto *SC = dyn_cast<SymbolCast>(*SI)) { + if (isTainted(State, SC->getOperand(), Kind)) + return true; + } + } + + return false; +} + +std::shared_ptr<PathDiagnosticPiece> +TaintBugVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + BugReport &BR) { + + // Find the ExplodedNode where the taint was first introduced + if (!isTainted(N->getState(), V) || + isTainted(N->getFirstPred()->getState(), V)) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + const LocationContext *NCtx = N->getLocationContext(); + PathDiagnosticLocation L = + PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here"); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Taint.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Taint.h new file mode 100644 index 000000000000..72cf6a79d52c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Taint.h @@ -0,0 +1,102 @@ +//=== Taint.h - Taint tracking and basic propagation rules. --------*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines basic, non-domain-specific mechanisms for tracking tainted values. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H + +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { +namespace ento { +namespace taint { + +/// The type of taint, which helps to differentiate between different types of +/// taint. +using TaintTagType = unsigned; + +static constexpr TaintTagType TaintTagGeneric = 0; + +/// Create a new state in which the value of the statement is marked as tainted. +LLVM_NODISCARD ProgramStateRef +addTaint(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx, + TaintTagType Kind = TaintTagGeneric); + +/// Create a new state in which the value is marked as tainted. +LLVM_NODISCARD ProgramStateRef +addTaint(ProgramStateRef State, SVal V, + TaintTagType Kind = TaintTagGeneric); + +/// Create a new state in which the symbol is marked as tainted. +LLVM_NODISCARD ProgramStateRef +addTaint(ProgramStateRef State, SymbolRef Sym, + TaintTagType Kind = TaintTagGeneric); + +/// Create a new state in which the pointer represented by the region +/// is marked as tainted. +LLVM_NODISCARD ProgramStateRef +addTaint(ProgramStateRef State, const MemRegion *R, + TaintTagType Kind = TaintTagGeneric); + +/// Create a new state in a which a sub-region of a given symbol is tainted. +/// This might be necessary when referring to regions that can not have an +/// individual symbol, e.g. if they are represented by the default binding of +/// a LazyCompoundVal. +LLVM_NODISCARD ProgramStateRef +addPartialTaint(ProgramStateRef State, + SymbolRef ParentSym, const SubRegion *SubRegion, + TaintTagType Kind = TaintTagGeneric); + +/// Check if the statement has a tainted value in the given state. +bool isTainted(ProgramStateRef State, const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind = TaintTagGeneric); + +/// Check if the value is tainted in the given state. +bool isTainted(ProgramStateRef State, SVal V, + TaintTagType Kind = TaintTagGeneric); + +/// Check if the symbol is tainted in the given state. +bool isTainted(ProgramStateRef State, SymbolRef Sym, + TaintTagType Kind = TaintTagGeneric); + +/// Check if the pointer represented by the region is tainted in the given +/// state. +bool isTainted(ProgramStateRef State, const MemRegion *Reg, + TaintTagType Kind = TaintTagGeneric); + +void printTaint(ProgramStateRef State, raw_ostream &Out, const char *nl = "\n", + const char *sep = ""); + +LLVM_DUMP_METHOD void dumpTaint(ProgramStateRef State); + +/// The bug visitor prints a diagnostic message at the location where a given +/// variable was tainted. +class TaintBugVisitor final : public BugReporterVisitor { +private: + const SVal V; + +public: + TaintBugVisitor(const SVal V) : V(V) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +} // namespace taint +} // namespace ento +} // namespace clang + +#endif + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp index 3aa8e95d0ad0..094762e2faf8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -1,15 +1,16 @@ //== TaintTesterChecker.cpp ----------------------------------- -*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This checker can be used for testing how taint data is propagated. // //===----------------------------------------------------------------------===// + +#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -18,6 +19,7 @@ using namespace clang; using namespace ento; +using namespace taint; namespace { class TaintTesterChecker : public Checker< check::PostStmt<Expr> > { @@ -47,7 +49,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E, if (!State) return; - if (State->isTainted(E, C.getLocationContext())) { + if (isTainted(State, E, C.getLocationContext())) { if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, "tainted",N); @@ -60,3 +62,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E, void ento::registerTaintTesterChecker(CheckerManager &mgr) { mgr.registerChecker<TaintTesterChecker>(); } + +bool ento::shouldRegisterTaintTesterChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index 527e371571f1..7a33845a6a26 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -1,9 +1,8 @@ //== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -261,3 +260,7 @@ void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition, void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) { mgr.registerChecker<TestAfterDivZeroChecker>(); } + +bool ento::shouldRegisterTestAfterDivZeroChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp index 2f06469bb209..73183aa468f6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -1,9 +1,8 @@ //== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -65,6 +64,10 @@ void ento::registerTraversalDumper(CheckerManager &mgr) { mgr.registerChecker<TraversalDumper>(); } +bool ento::shouldRegisterTraversalDumper(const LangOptions &LO) { + return true; +} + //------------------------------------------------------------------------------ namespace { @@ -112,3 +115,7 @@ void CallDumper::checkPostCall(const CallEvent &Call, CheckerContext &C) const { void ento::registerCallDumper(CheckerManager &mgr) { mgr.registerChecker<CallDumper>(); } + +bool ento::shouldRegisterCallDumper(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp index 5e777803af00..62a4c2ab0209 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp @@ -1,9 +1,8 @@ //== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -88,7 +87,7 @@ public: if (isNonNullPtr(Call, C)) if (auto L = Call.getReturnValue().getAs<Loc>()) - State = State->assume(*L, /*Assumption=*/true); + State = State->assume(*L, /*assumption=*/true); C.addTransition(State); } @@ -107,7 +106,7 @@ public: (Msg.getSelector() == SetObjectForKeyedSubscriptSel || Msg.getSelector() == SetObjectForKeySel)) { if (auto L = Msg.getArgSVal(1).getAs<Loc>()) - State = State->assume(*L, /*Assumption=*/true); + State = State->assume(*L, /*assumption=*/true); } // Record an implication: index is non-null if the output is non-null. @@ -249,7 +248,10 @@ private: } // end empty namespace - void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext()); } + +bool ento::shouldRegisterTrustNonnullChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index d7fad4e475ab..3a4a1dbf641b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -1,9 +1,8 @@ //=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -109,3 +108,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, void ento::registerUndefBranchChecker(CheckerManager &mgr) { mgr.registerChecker<UndefBranchChecker>(); } + +bool ento::shouldRegisterUndefBranchChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 8a625227b81e..c787ef58660a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -1,9 +1,8 @@ // UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -100,3 +99,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) { mgr.registerChecker<UndefCapturedBlockVarChecker>(); } + +bool ento::shouldRegisterUndefCapturedBlockVarChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 624cff6048fd..1ae287d39f11 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -1,9 +1,8 @@ //=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -186,3 +185,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, void ento::registerUndefResultChecker(CheckerManager &mgr) { mgr.registerChecker<UndefResultChecker>(); } + +bool ento::shouldRegisterUndefResultChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index 1d78d7cebd67..4c517d6f0562 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -1,9 +1,8 @@ //===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -62,3 +61,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, void ento::registerUndefinedArraySubscriptChecker(CheckerManager &mgr) { mgr.registerChecker<UndefinedArraySubscriptChecker>(); } + +bool ento::shouldRegisterUndefinedArraySubscriptChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 8e10bfdd2f3c..d32d2a4042de 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -1,9 +1,8 @@ //===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -120,3 +119,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) { mgr.registerChecker<UndefinedAssignmentChecker>(); } + +bool ento::shouldRegisterUndefinedAssignmentChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h index c3291a21c164..2fcdd6086309 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h @@ -1,9 +1,8 @@ //===----- UninitializedObject.h ---------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -18,7 +17,7 @@ // won't emit warnings for objects that don't have at least one initialized // field. This may be set with // -// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`. +// `-analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true`. // // - "NotesAsWarnings" (boolean). If set to true, the checker will emit a // warning for each uninitialized field, as opposed to emitting one warning @@ -26,27 +25,34 @@ // to it in notes. Defaults to false. // // `-analyzer-config \ -// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`. +// optin.cplusplus.UninitializedObject:NotesAsWarnings=true`. // // - "CheckPointeeInitialization" (boolean). If set to false, the checker will // not analyze the pointee of pointer/reference fields, and will only check // whether the object itself is initialized. Defaults to false. // // `-analyzer-config \ -// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`. +// optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true`. +// +// TODO: With some clever heuristics, some pointers should be dereferenced +// by default. For example, if the pointee is constructed within the +// constructor call, it's reasonable to say that no external object +// references it, and we wouldn't generate multiple report on the same +// pointee. // // - "IgnoreRecordsWithField" (string). If supplied, the checker will not // analyze structures that have a field with a name or type name that // matches the given pattern. Defaults to "". // // `-analyzer-config \ -// alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`. +// optin.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`. // -// TODO: With some clever heuristics, some pointers should be dereferenced -// by default. For example, if the pointee is constructed within the -// constructor call, it's reasonable to say that no external object -// references it, and we wouldn't generate multiple report on the same -// pointee. +// - "IgnoreGuardedFields" (boolean). If set to true, the checker will analyze +// _syntactically_ whether the found uninitialized object is used without a +// preceding assert call. Defaults to false. +// +// `-analyzer-config \ +// optin.cplusplus.UninitializedObject:IgnoreGuardedFields=true`. // // Most of the following methods as well as the checker itself is defined in // UninitializedObjectChecker.cpp. @@ -69,6 +75,7 @@ struct UninitObjCheckerOptions { bool ShouldConvertNotesToWarnings = false; bool CheckPointeeInitialization = false; std::string IgnoredRecordsWithFieldPattern; + bool IgnoreGuardedFields = false; }; /// A lightweight polymorphic wrapper around FieldRegion *. We'll use this @@ -316,8 +323,8 @@ private: /// needs to be analyzed as much as checking whether their value is undefined. inline bool isPrimitiveType(const QualType &T) { return T->isBuiltinType() || T->isEnumeralType() || - T->isMemberPointerType() || T->isBlockPointerType() || - T->isFunctionType(); + T->isFunctionType() || T->isAtomicType() || + T->isVectorType() || T->isScalarType(); } inline bool isDereferencableType(const QualType &T) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp index 208e303e8295..9d608c12d19b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -1,9 +1,8 @@ //===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -20,6 +19,8 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "UninitializedObject.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -27,6 +28,7 @@ using namespace clang; using namespace clang::ento; +using namespace clang::ast_matchers; /// We'll mark fields (and pointee of fields) that are confirmed to be /// uninitialized as already analyzed. @@ -119,6 +121,16 @@ static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, /// \p Pattern. static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern); +/// Checks _syntactically_ whether it is possible to access FD from the record +/// that contains it without a preceding assert (even if that access happens +/// inside a method). This is mainly used for records that act like unions, like +/// having multiple bit fields, with only a fraction being properly initialized. +/// If these fields are properly guarded with asserts, this method returns +/// false. +/// +/// Since this check is done syntactically, this method could be inaccurate. +static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State); + //===----------------------------------------------------------------------===// // Methods for UninitializedObjectChecker. //===----------------------------------------------------------------------===// @@ -235,6 +247,13 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain, "One must also pass the pointee region as a parameter for " "dereferenceable fields!"); + if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( + FR->getDecl()->getLocation())) + return false; + + if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State)) + return false; + if (State->contains<AnalyzedRegions>(FR)) return false; @@ -247,13 +266,10 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain, State = State->add<AnalyzedRegions>(FR); - if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( - FR->getDecl()->getLocation())) - return false; - UninitFieldMap::mapped_type NoteMsgBuf; llvm::raw_svector_ostream OS(NoteMsgBuf); Chain.printNoteMsg(OS); + return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second; } @@ -442,8 +458,8 @@ static const TypedValueRegion * getConstructedRegion(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) { - Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl, - Context.getStackFrame()); + Loc ThisLoc = + Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame()); SVal ObjectV = Context.getState()->getSVal(ThisLoc); @@ -496,6 +512,75 @@ static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) { return false; } +static const Stmt *getMethodBody(const CXXMethodDecl *M) { + if (isa<CXXConstructorDecl>(M)) + return nullptr; + + if (!M->isDefined()) + return nullptr; + + return M->getDefinition()->getBody(); +} + +static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) { + + if (FD->getAccess() == AccessSpecifier::AS_public) + return true; + + const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent()); + + if (!Parent) + return true; + + Parent = Parent->getDefinition(); + assert(Parent && "The record's definition must be avaible if an uninitialized" + " field of it was found!"); + + ASTContext &AC = State->getStateManager().getContext(); + + auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access"); + + auto AssertLikeM = callExpr(callee(functionDecl( + anyOf(hasName("exit"), hasName("panic"), hasName("error"), + hasName("Assert"), hasName("assert"), hasName("ziperr"), + hasName("assfail"), hasName("db_error"), hasName("__assert"), + hasName("__assert2"), hasName("_wassert"), hasName("__assert_rtn"), + hasName("__assert_fail"), hasName("dtrace_assfail"), + hasName("yy_fatal_error"), hasName("_XCAssertionFailureHandler"), + hasName("_DTAssertionFailureHandler"), + hasName("_TSAssertionFailureHandler"))))); + + auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn()))); + + auto GuardM = + stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM, + NoReturnFuncM)) + .bind("guard"); + + for (const CXXMethodDecl *M : Parent->methods()) { + const Stmt *MethodBody = getMethodBody(M); + if (!MethodBody) + continue; + + auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC); + if (Accesses.empty()) + continue; + const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access"); + assert(FirstAccess); + + auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC); + if (Guards.empty()) + return true; + const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard"); + assert(FirstGuard); + + if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc()) + return true; + } + + return false; +} + std::string clang::ento::getVariableName(const FieldDecl *Field) { // If Field is a captured lambda variable, Field->getName() will return with // an empty string. We can however acquire it's name from the lambda's @@ -526,13 +611,23 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions(); UninitObjCheckerOptions &ChOpts = Chk->Opts; - ChOpts.IsPedantic = - AnOpts.getCheckerBooleanOption("Pedantic", /*DefaultVal*/ false, Chk); - ChOpts.ShouldConvertNotesToWarnings = - AnOpts.getCheckerBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk); + ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic"); + ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption( + Chk, "NotesAsWarnings"); ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption( - "CheckPointeeInitialization", /*DefaultVal*/ false, Chk); + Chk, "CheckPointeeInitialization"); ChOpts.IgnoredRecordsWithFieldPattern = - AnOpts.getCheckerStringOption("IgnoreRecordsWithField", - /*DefaultVal*/ "", Chk); + AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField"); + ChOpts.IgnoreGuardedFields = + AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields"); + + std::string ErrorMsg; + if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg)) + Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField", + "a valid regex, building failed with error message " + "\"" + ErrorMsg + "\""); +} + +bool ento::shouldRegisterUninitializedObjectChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp index aead59c7bf87..a5dc250104f3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -1,9 +1,8 @@ //===----- UninitializedPointee.cpp ------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index bab0c12704fa..2ccb519891f3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -1,9 +1,8 @@ //= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -21,10 +20,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" -#include <fcntl.h> using namespace clang; using namespace ento; @@ -40,8 +36,9 @@ enum class OpenVariant { }; namespace { -class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { - mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; + +class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { + mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; mutable Optional<uint64_t> Val_O_CREAT; public: @@ -51,8 +48,25 @@ public: void CheckOpen(CheckerContext &C, const CallExpr *CE) const; void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const; - void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; + + void CheckOpenVariant(CheckerContext &C, + const CallExpr *CE, OpenVariant Variant) const; + + void ReportOpenBug(CheckerContext &C, + ProgramStateRef State, + const char *Msg, + SourceRange SR) const; + +}; + +class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > { +public: + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + +private: + mutable std::unique_ptr<BugType> BT_mallocZero; + void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; @@ -61,13 +75,6 @@ public: void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const; void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; - typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, - const CallExpr *) const; -private: - - void CheckOpenVariant(CheckerContext &C, - const CallExpr *CE, OpenVariant Variant) const; - bool ReportZeroByteAllocation(CheckerContext &C, ProgramStateRef falseState, const Expr *arg, @@ -77,48 +84,75 @@ private: const unsigned numArgs, const unsigned sizeArg, const char *fn) const; - void LazyInitialize(std::unique_ptr<BugType> &BT, const char *name) const { - if (BT) - return; - BT.reset(new BugType(this, name, categories::UnixAPI)); - } - void ReportOpenBug(CheckerContext &C, - ProgramStateRef State, - const char *Msg, - SourceRange SR) const; }; + } //end anonymous namespace +static void LazyInitialize(const CheckerBase *Checker, + std::unique_ptr<BugType> &BT, + const char *name) { + if (BT) + return; + BT.reset(new BugType(Checker, name, categories::UnixAPI)); +} + //===----------------------------------------------------------------------===// // "open" (man 2 open) -//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===/ -void UnixAPIChecker::ReportOpenBug(CheckerContext &C, - ProgramStateRef State, - const char *Msg, - SourceRange SR) const { +void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + // Don't treat functions in namespaces with the same name a Unix function + // as a call to the Unix function. + const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); + if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) + return; + + StringRef FName = C.getCalleeName(FD); + if (FName.empty()) + return; + + if (FName == "open") + CheckOpen(C, CE); + + else if (FName == "openat") + CheckOpenAt(C, CE); + + else if (FName == "pthread_once") + CheckPthreadOnce(C, CE); +} +void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, + ProgramStateRef State, + const char *Msg, + SourceRange SR) const { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; - LazyInitialize(BT_open, "Improper use of 'open'"); + LazyInitialize(this, BT_open, "Improper use of 'open'"); auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); Report->addRange(SR); C.emitReport(std::move(Report)); } -void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { +void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C, + const CallExpr *CE) const { CheckOpenVariant(C, CE, OpenVariant::Open); } -void UnixAPIChecker::CheckOpenAt(CheckerContext &C, const CallExpr *CE) const { +void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C, + const CallExpr *CE) const { CheckOpenVariant(C, CE, OpenVariant::OpenAt); } -void UnixAPIChecker::CheckOpenVariant(CheckerContext &C, - const CallExpr *CE, - OpenVariant Variant) const { +void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, + const CallExpr *CE, + OpenVariant Variant) const { // The index of the argument taking the flags open flags (O_RDONLY, // O_WRONLY, O_CREAT, etc.), unsigned int FlagsArgIndex; @@ -236,7 +270,7 @@ void UnixAPIChecker::CheckOpenVariant(CheckerContext &C, // pthread_once //===----------------------------------------------------------------------===// -void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, +void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const { // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. @@ -268,7 +302,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; - LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); + LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); @@ -279,15 +313,16 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" // with allocation size 0 //===----------------------------------------------------------------------===// + // FIXME: Eventually these should be rolled into the MallocChecker, but right now // they're more basic and valuable for widespread use. // Returns true if we try to do a zero byte allocation, false otherwise. // Fills in trueState and falseState. static bool IsZeroByteAllocation(ProgramStateRef state, - const SVal argVal, - ProgramStateRef *trueState, - ProgramStateRef *falseState) { + const SVal argVal, + ProgramStateRef *trueState, + ProgramStateRef *falseState) { std::tie(*trueState, *falseState) = state->assume(argVal.castAs<DefinedSVal>()); @@ -297,15 +332,16 @@ static bool IsZeroByteAllocation(ProgramStateRef state, // Generates an error report, indicating that the function whose name is given // will perform a zero byte allocation. // Returns false if an error occurred, true otherwise. -bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, - ProgramStateRef falseState, - const Expr *arg, - const char *fn_name) const { +bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( + CheckerContext &C, + ProgramStateRef falseState, + const Expr *arg, + const char *fn_name) const { ExplodedNode *N = C.generateErrorNode(falseState); if (!N) return false; - LazyInitialize(BT_mallocZero, + LazyInitialize(this, BT_mallocZero, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); SmallString<256> S; @@ -322,11 +358,11 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, // Does a basic check for 0-sized allocations suitable for most of the below // functions (modulo "calloc") -void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, - const CallExpr *CE, - const unsigned numArgs, - const unsigned sizeArg, - const char *fn) const { +void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned numArgs, + const unsigned sizeArg, + const char *fn) const { // Sanity check for the correct number of arguments if (CE->getNumArgs() != numArgs) return; @@ -351,8 +387,8 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, C.addTransition(trueState); } -void UnixAPIChecker::CheckCallocZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C, + const CallExpr *CE) const { unsigned int nArgs = CE->getNumArgs(); if (nArgs != 2) return; @@ -387,43 +423,39 @@ void UnixAPIChecker::CheckCallocZero(CheckerContext &C, C.addTransition(trueState); } -void UnixAPIChecker::CheckMallocZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C, + const CallExpr *CE) const { BasicAllocationCheck(C, CE, 1, 0, "malloc"); } -void UnixAPIChecker::CheckReallocZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C, + const CallExpr *CE) const { BasicAllocationCheck(C, CE, 2, 1, "realloc"); } -void UnixAPIChecker::CheckReallocfZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C, + const CallExpr *CE) const { BasicAllocationCheck(C, CE, 2, 1, "reallocf"); } -void UnixAPIChecker::CheckAllocaZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C, + const CallExpr *CE) const { BasicAllocationCheck(C, CE, 1, 0, "alloca"); } -void UnixAPIChecker::CheckAllocaWithAlignZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero( + CheckerContext &C, + const CallExpr *CE) const { BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); } -void UnixAPIChecker::CheckVallocZero(CheckerContext &C, - const CallExpr *CE) const { +void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C, + const CallExpr *CE) const { BasicAllocationCheck(C, CE, 1, 0, "valloc"); } - -//===----------------------------------------------------------------------===// -// Central dispatch function. -//===----------------------------------------------------------------------===// - -void UnixAPIChecker::checkPreStmt(const CallExpr *CE, - CheckerContext &C) const { +void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; @@ -438,42 +470,40 @@ void UnixAPIChecker::checkPreStmt(const CallExpr *CE, if (FName.empty()) return; - if (CheckMisuse) { - if (SubChecker SC = - llvm::StringSwitch<SubChecker>(FName) - .Case("open", &UnixAPIChecker::CheckOpen) - .Case("openat", &UnixAPIChecker::CheckOpenAt) - .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) - .Default(nullptr)) { - (this->*SC)(C, CE); - } - } - if (CheckPortability) { - if (SubChecker SC = - llvm::StringSwitch<SubChecker>(FName) - .Case("calloc", &UnixAPIChecker::CheckCallocZero) - .Case("malloc", &UnixAPIChecker::CheckMallocZero) - .Case("realloc", &UnixAPIChecker::CheckReallocZero) - .Case("reallocf", &UnixAPIChecker::CheckReallocfZero) - .Cases("alloca", "__builtin_alloca", - &UnixAPIChecker::CheckAllocaZero) - .Case("__builtin_alloca_with_align", - &UnixAPIChecker::CheckAllocaWithAlignZero) - .Case("valloc", &UnixAPIChecker::CheckVallocZero) - .Default(nullptr)) { - (this->*SC)(C, CE); - } - } + if (FName == "calloc") + CheckCallocZero(C, CE); + + else if (FName == "malloc") + CheckMallocZero(C, CE); + + else if (FName == "realloc") + CheckReallocZero(C, CE); + + else if (FName == "reallocf") + CheckReallocfZero(C, CE); + + else if (FName == "alloca" || FName == "__builtin_alloca") + CheckAllocaZero(C, CE); + + else if (FName == "__builtin_alloca_with_align") + CheckAllocaWithAlignZero(C, CE); + + else if (FName == "valloc") + CheckVallocZero(C, CE); } //===----------------------------------------------------------------------===// // Registration. //===----------------------------------------------------------------------===// -#define REGISTER_CHECKER(Name) \ - void ento::registerUnixAPI##Name##Checker(CheckerManager &mgr) { \ - mgr.registerChecker<UnixAPIChecker>()->Check##Name = true; \ +#define REGISTER_CHECKER(CHECKERNAME) \ + void ento::register##CHECKERNAME(CheckerManager &mgr) { \ + mgr.registerChecker<CHECKERNAME>(); \ + } \ + \ + bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \ + return true; \ } -REGISTER_CHECKER(Misuse) -REGISTER_CHECKER(Portability) +REGISTER_CHECKER(UnixAPIMisuseChecker) +REGISTER_CHECKER(UnixAPIPortabilityChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 16b4d5e925ba..0b0bf8465c9d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -1,9 +1,8 @@ //==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file implements a generalized unreachable code checker using a @@ -205,7 +204,7 @@ const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { return S->getStmt(); } } - if (const Stmt *S = CB->getTerminator()) + if (const Stmt *S = CB->getTerminatorStmt()) return S; else return nullptr; @@ -251,9 +250,13 @@ bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { return CB->getLabel() == nullptr // No labels && CB->size() == 0 // No statements - && !CB->getTerminator(); // No terminator + && !CB->getTerminatorStmt(); // No terminator } void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { mgr.registerChecker<UnreachableCodeChecker>(); } + +bool ento::shouldRegisterUnreachableCodeChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index e458e0554ee2..1630896c3b60 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -1,9 +1,8 @@ //=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,6 +13,7 @@ // //===----------------------------------------------------------------------===// +#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/CharUnits.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -26,6 +26,7 @@ using namespace clang; using namespace ento; +using namespace taint; namespace { class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > { @@ -107,7 +108,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { return; // Check if the size is tainted. - if (state->isTainted(sizeV)) { + if (isTainted(state, sizeV)) { reportBug(VLA_Tainted, SE, nullptr, C, llvm::make_unique<TaintBugVisitor>(sizeV)); return; @@ -183,3 +184,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { void ento::registerVLASizeChecker(CheckerManager &mgr) { mgr.registerChecker<VLASizeChecker>(); } + +bool ento::shouldRegisterVLASizeChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 748b226b7a1e..13ad3d98e8eb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -1,9 +1,8 @@ //== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -276,8 +275,8 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, new BugType(CheckNames[CK_Unterminated].getName().empty() ? CheckNames[CK_Uninitialized] : CheckNames[CK_Unterminated], - "Leaked va_list", categories::MemoryError)); - BT_leakedvalist->setSuppressOnSink(true); + "Leaked va_list", categories::MemoryError, + /*SuppressOnSink=*/true)); } const ExplodedNode *StartNode = getStartCallSite(N, Reg); @@ -400,11 +399,23 @@ std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode( return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true); } +void ento::registerValistBase(CheckerManager &mgr) { + mgr.registerChecker<ValistChecker>(); +} + +bool ento::shouldRegisterValistBase(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name##Checker(CheckerManager &mgr) { \ - ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \ + ValistChecker *checker = mgr.getChecker<ValistChecker>(); \ checker->ChecksEnabled[ValistChecker::CK_##name] = true; \ checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \ + } \ + \ + bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \ + return true; \ } REGISTER_CHECKER(Uninitialized) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp index 3ee9f1a07fa2..40d14aa5c7d4 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -1,9 +1,8 @@ //===- VforkChecker.cpp -------- Vfork usage checks --------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -216,3 +215,7 @@ void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { void ento::registerVforkChecker(CheckerManager &mgr) { mgr.registerChecker<VforkChecker>(); } + +bool ento::shouldRegisterVforkChecker(const LangOptions &LO) { + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index 567063197405..762c9c1c8d7a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -1,9 +1,8 @@ //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -280,6 +279,9 @@ void ento::registerVirtualCallChecker(CheckerManager &mgr) { VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>(); checker->IsPureOnly = - mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false, - checker); + mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "PureOnly"); +} + +bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) { + return true; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp index c7e95268213e..a1de10c89ed9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -1,9 +1,8 @@ //===--- APSIntType.cpp - Simple record of the type of APSInts ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 7fb1c09ca049..1b1ffff5ade8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -1,9 +1,8 @@ //===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -24,7 +23,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, : AnaCtxMgr( ASTCtx, Options.UnoptimizedCFG, Options.ShouldIncludeImplicitDtorsInCFG, - /*AddInitializers=*/true, + /*addInitializers=*/true, Options.ShouldIncludeTemporaryDtorsInCFG, Options.ShouldIncludeLifetimeInCFG, // Adding LoopExit elements to the CFG is a requirement for loop @@ -36,7 +35,9 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, Options.ShouldConditionalizeStaticInitializers, /*addCXXNewAllocator=*/true, Options.ShouldIncludeRichConstructorsInCFG, - Options.ShouldElideConstructors, injector), + Options.ShouldElideConstructors, + /*addVirtualBaseBranches=*/true, + injector), Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 0588c2bd3d35..71abe2ae6c0e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -1,9 +1,8 @@ //===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -20,6 +19,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" #include <cassert> #include <cstddef> @@ -34,7 +34,7 @@ std::vector<StringRef> AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { static const StringRef StaticAnalyzerChecks[] = { #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ FULLNAME, #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -49,6 +49,37 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { return Result; } +void AnalyzerOptions::printFormattedEntry( + llvm::raw_ostream &Out, + std::pair<StringRef, StringRef> EntryDescPair, + size_t InitialPad, size_t EntryWidth, size_t MinLineWidth) { + + llvm::formatted_raw_ostream FOut(Out); + + const size_t PadForDesc = InitialPad + EntryWidth; + + FOut.PadToColumn(InitialPad) << EntryDescPair.first; + // If the buffer's length is greater then PadForDesc, print a newline. + if (FOut.getColumn() > PadForDesc) + FOut << '\n'; + + FOut.PadToColumn(PadForDesc); + + if (MinLineWidth == 0) { + FOut << EntryDescPair.second; + return; + } + + for (char C : EntryDescPair.second) { + if (FOut.getColumn() > MinLineWidth && C == ' ') { + FOut << '\n'; + FOut.PadToColumn(PadForDesc); + continue; + } + FOut << C; + } +} + ExplorationStrategyKind AnalyzerOptions::getExplorationStrategy() const { auto K = @@ -102,18 +133,13 @@ AnalyzerOptions::mayInlineCXXMemberFunction( return *K >= Param; } -StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, - StringRef DefaultVal, - const CheckerBase *C, +StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName, + StringRef OptionName, bool SearchInParents) const { - assert(C); - // Search for a package option if the option for the checker is not specified - // and search in parents is enabled. - StringRef CheckerName = C->getTagDescription(); - assert(!CheckerName.empty() && "Empty checker name! Make sure the checker object (including it's " "bases!) if fully initialized before calling this function!"); + ConfigTable::const_iterator E = Config.end(); do { ConfigTable::const_iterator I = @@ -122,35 +148,66 @@ StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, return StringRef(I->getValue()); size_t Pos = CheckerName.rfind('.'); if (Pos == StringRef::npos) - return DefaultVal; + break; + CheckerName = CheckerName.substr(0, Pos); } while (!CheckerName.empty() && SearchInParents); - return DefaultVal; + + llvm_unreachable("Unknown checker option! Did you call getChecker*Option " + "with incorrect parameters? User input must've been " + "verified by CheckerRegistry."); + + return ""; } -bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal, - const CheckerBase *C, +StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C, + StringRef OptionName, + bool SearchInParents) const { + return getCheckerStringOption( + C->getTagDescription(), OptionName, SearchInParents); +} + +bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName, + StringRef OptionName, bool SearchInParents) const { - // FIXME: We should emit a warning here if the value is something other than - // "true", "false", or the empty string (meaning the default value), - // but the AnalyzerOptions doesn't have access to a diagnostic engine. - assert(C); - return llvm::StringSwitch<bool>( - getCheckerStringOption(Name, DefaultVal ? "true" : "false", C, + auto Ret = llvm::StringSwitch<llvm::Optional<bool>>( + getCheckerStringOption(CheckerName, OptionName, SearchInParents)) .Case("true", true) .Case("false", false) - .Default(DefaultVal); + .Default(None); + + assert(Ret && + "This option should be either 'true' or 'false', and should've been " + "validated by CheckerRegistry!"); + + return *Ret; +} + +bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C, + StringRef OptionName, + bool SearchInParents) const { + return getCheckerBooleanOption( + C->getTagDescription(), OptionName, SearchInParents); } -int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal, - const CheckerBase *C, - bool SearchInParents) const { - int Ret = DefaultVal; - bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C, +int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName, + StringRef OptionName, + bool SearchInParents) const { + int Ret = 0; + bool HasFailed = getCheckerStringOption(CheckerName, OptionName, SearchInParents) - .getAsInteger(10, Ret); - assert(!HasFailed && "analyzer-config option should be numeric"); + .getAsInteger(0, Ret); + assert(!HasFailed && + "This option should be numeric, and should've been validated by " + "CheckerRegistry!"); (void)HasFailed; return Ret; } + +int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C, + StringRef OptionName, + bool SearchInParents) const { + return getCheckerIntegerOption( + C->getTagDescription(), OptionName, SearchInParents); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index d8ed6942de81..7cd48bf44374 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -1,9 +1,8 @@ //===- BasicValueFactory.cpp - Basic values for Path Sens analysis --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -232,9 +231,6 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. - if (V1.isSigned() && V1.isNegative()) - return nullptr; - if (V2.isSigned() && V2.isNegative()) return nullptr; @@ -243,8 +239,13 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, if (Amt >= V1.getBitWidth()) return nullptr; - if (V1.isSigned() && Amt > V1.countLeadingZeros()) + if (!Ctx.getLangOpts().CPlusPlus2a) { + if (V1.isSigned() && V1.isNegative()) + return nullptr; + + if (V1.isSigned() && Amt > V1.countLeadingZeros()) return nullptr; + } return &getValue( V1.operator<<( (unsigned) Amt )); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp index 8c99bd808494..e7ac6f1cfa00 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -1,9 +1,8 @@ //==- BlockCounter.h - ADT for counting block visits -------------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index fd7f53210490..e5a0794f10e2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1,9 +1,8 @@ //===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -155,8 +154,6 @@ static void removeRedundantMsgs(PathPieces &path) { case PathDiagnosticPiece::Macro: removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(*piece).subPieces); break; - case PathDiagnosticPiece::ControlFlow: - break; case PathDiagnosticPiece::Event: { if (i == N-1) break; @@ -176,7 +173,9 @@ static void removeRedundantMsgs(PathPieces &path) { } break; } + case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: break; } path.push_back(std::move(piece)); @@ -231,9 +230,8 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, break; } case PathDiagnosticPiece::ControlFlow: - break; - case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: break; } @@ -243,6 +241,16 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, return containsSomethingInteresting; } +/// Same logic as above to remove extra pieces. +static void removePopUpNotes(PathPieces &Path) { + for (unsigned int i = 0; i < Path.size(); ++i) { + auto Piece = std::move(Path.front()); + Path.pop_front(); + if (!isa<PathDiagnosticPopUpPiece>(*Piece)) + Path.push_back(std::move(Piece)); + } +} + /// Returns true if the given decl has been implicitly given a body, either by /// the analyzer or by the compiler proper. static bool hasImplicitBody(const Decl *D) { @@ -679,7 +687,7 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, const LocationContext *LC = N->getLocationContext(); const CFGBlock *Src = BE.getSrc(); const CFGBlock *Dst = BE.getDst(); - const Stmt *T = Src->getTerminator(); + const Stmt *T = Src->getTerminatorStmt(); if (!T) return; @@ -1204,7 +1212,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, const CFGBlock *BSrc = BE->getSrc(); ParentMap &PM = PDB.getParentMap(); - if (const Stmt *Term = BSrc->getTerminator()) { + if (const Stmt *Term = BSrc->getTerminatorStmt()) { // Are we jumping past the loop body without ever executing the // loop (because the condition was false)? if (isLoop(Term)) { @@ -1247,11 +1255,11 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, static std::unique_ptr<PathDiagnostic> generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { - BugType &BT = R->getBugType(); + const BugType &BT = R->getBugType(); return llvm::make_unique<PathDiagnostic>( R->getBugType().getCheckName(), R->getDeclWithIssue(), R->getBugType().getName(), R->getDescription(), - R->getShortDescription(/*Fallback=*/false), BT.getCategory(), + R->getShortDescription(/*UseFallback=*/false), BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(), findExecutedLines(SM, R->getErrorNode())); } @@ -1370,8 +1378,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, break; // If the source is in the same context, we're already good. - if (std::find(SrcContexts.begin(), SrcContexts.end(), DstContext) != - SrcContexts.end()) + if (llvm::find(SrcContexts, DstContext) != SrcContexts.end()) break; // Update the subexpression node to point to the context edge. @@ -1983,6 +1990,10 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( (void)stillHasNotes; } + // Remove pop-up notes if needed. + if (!Opts.ShouldAddPopUpNotes) + removePopUpNotes(PD->getMutablePieces()); + // Redirect all call pieces to have valid locations. adjustCallLocations(PD->getMutablePieces()); removePiecesWithInvalidLocations(PD->getMutablePieces()); @@ -2612,7 +2623,7 @@ std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( // Register additional node visitors. R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); - R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>()); + R->addVisitor(llvm::make_unique<TagVisitor>()); BugReporterContext BRC(Reporter, ErrorGraph.BackMap); @@ -2684,7 +2695,7 @@ GRBugReporter::generatePathDiagnostics( return Out; } -void BugReporter::Register(BugType *BT) { +void BugReporter::Register(const BugType *BT) { BugTypes = F.add(BugTypes, BT); } @@ -2718,7 +2729,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - BugType& BT = R->getBugType(); + const BugType& BT = R->getBugType(); Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2836,7 +2847,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, SmallVectorImpl<BugReport*> &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - BugType& BT = I->getBugType(); + const BugType& BT = I->getBugType(); // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index ea695c4736a3..250793c4baff 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1,9 +1,8 @@ //===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/Analyses/Dominators.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" @@ -162,8 +162,8 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { /// are the immediate snapshots of the tracked region's bindings within the /// node's respective states but not really checking that these snapshots /// actually contain the same set of bindings. -bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, - const ExplodedNode *RightNode, SVal RightVal) { +static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, + const ExplodedNode *RightNode, SVal RightVal) { if (LeftVal == RightVal) return true; @@ -180,35 +180,21 @@ bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, RLCV->getStore() == RightNode->getState()->getStore(); } -//===----------------------------------------------------------------------===// -// Definitions for bug reporter visitors. -//===----------------------------------------------------------------------===// - -std::shared_ptr<PathDiagnosticPiece> -BugReporterVisitor::getEndPath(BugReporterContext &, - const ExplodedNode *, BugReport &) { - return nullptr; -} - -void -BugReporterVisitor::finalizeVisitor(BugReporterContext &, - const ExplodedNode *, BugReport &) {} - -std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( - BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { - PathDiagnosticLocation L = - PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); - - const auto &Ranges = BR.getRanges(); +static Optional<const llvm::APSInt *> +getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { + ProgramStateRef State = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); - // Only add the statement itself as a range if we didn't specify any - // special ranges for this report. - auto P = std::make_shared<PathDiagnosticEventPiece>( - L, BR.getDescription(), Ranges.begin() == Ranges.end()); - for (SourceRange Range : Ranges) - P->addRange(Range); + // The declaration of the value may rely on a pointer so take its l-value. + if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(CondVarExpr)) { + if (const auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) { + SVal DeclSVal = State->getSVal(State->getLValue(VD, LCtx)); + if (auto DeclCI = DeclSVal.getAs<nonloc::ConcreteInt>()) + return &DeclCI->getValue(); + } + } - return P; + return {}; } /// \return name of the macro inside the location \p Loc. @@ -235,36 +221,70 @@ static bool isFunctionMacroExpansion(SourceLocation Loc, } /// \return Whether \c RegionOfInterest was modified at \p N, -/// where \p ReturnState is a state associated with the return -/// from the current frame. -static bool wasRegionOfInterestModifiedAt( - const SubRegion *RegionOfInterest, - const ExplodedNode *N, - SVal ValueAfter) { +/// where \p ValueAfter is \c RegionOfInterest's value at the end of the +/// stack frame. +static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest, + const ExplodedNode *N, + SVal ValueAfter) { ProgramStateRef State = N->getState(); ProgramStateManager &Mgr = N->getState()->getStateManager(); - if (!N->getLocationAs<PostStore>() - && !N->getLocationAs<PostInitializer>() - && !N->getLocationAs<PostStmt>()) + if (!N->getLocationAs<PostStore>() && !N->getLocationAs<PostInitializer>() && + !N->getLocationAs<PostStmt>()) return false; // Writing into region of interest. if (auto PS = N->getLocationAs<PostStmt>()) if (auto *BO = PS->getStmtAs<BinaryOperator>()) if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf( - N->getSVal(BO->getLHS()).getAsRegion())) + N->getSVal(BO->getLHS()).getAsRegion())) return true; // SVal after the state is possibly different. SVal ValueAtN = N->getState()->getSVal(RegionOfInterest); - if (!Mgr.getSValBuilder().areEqual(State, ValueAtN, ValueAfter).isConstrainedTrue() && + if (!Mgr.getSValBuilder() + .areEqual(State, ValueAtN, ValueAfter) + .isConstrainedTrue() && (!ValueAtN.isUndef() || !ValueAfter.isUndef())) return true; return false; } +//===----------------------------------------------------------------------===// +// Implementation of BugReporterVisitor. +//===----------------------------------------------------------------------===// + +std::shared_ptr<PathDiagnosticPiece> +BugReporterVisitor::getEndPath(BugReporterContext &, + const ExplodedNode *, BugReport &) { + return nullptr; +} + +void +BugReporterVisitor::finalizeVisitor(BugReporterContext &, + const ExplodedNode *, BugReport &) {} + +std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( + BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); + + const auto &Ranges = BR.getRanges(); + + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + auto P = std::make_shared<PathDiagnosticEventPiece>( + L, BR.getDescription(), Ranges.begin() == Ranges.end()); + for (SourceRange Range : Ranges) + P->addRange(Range); + + return P; +} + +//===----------------------------------------------------------------------===// +// Implementation of NoStoreFuncVisitor. +//===----------------------------------------------------------------------===// namespace { @@ -295,6 +315,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; using RegionVector = SmallVector<const MemRegion *, 5>; + public: NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()), @@ -307,354 +328,418 @@ public: ID.AddPointer(RegionOfInterest); } + void *getTag() const { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, BugReporterContext &BR, - BugReport &) override { + BugReport &R) override; +private: + /// Attempts to find the region of interest in a given record decl, + /// by either following the base classes or fields. + /// Dereferences fields up to a given recursion limit. + /// Note that \p Vec is passed by value, leading to quadratic copying cost, + /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. + /// \return A chain fields leading to the region of interest or None. + const Optional<RegionVector> + findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, + const MemRegion *R, const RegionVector &Vec = {}, + int depth = 0); + + /// Check and lazily calculate whether the region of interest is + /// modified in the stack frame to which \p N belongs. + /// The calculation is cached in FramesModifyingRegion. + bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) { const LocationContext *Ctx = N->getLocationContext(); const StackFrameContext *SCtx = Ctx->getStackFrame(); - ProgramStateRef State = N->getState(); - auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + if (!FramesModifyingCalculated.count(SCtx)) + findModifyingFrames(N); + return FramesModifyingRegion.count(SCtx); + } - // No diagnostic if region was modified inside the frame. - if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N)) - return nullptr; + /// Write to \c FramesModifyingRegion all stack frames along + /// the path in the current stack frame which modify \c RegionOfInterest. + void findModifyingFrames(const ExplodedNode *N); - CallEventRef<> Call = - BR.getStateManager().getCallEventManager().getCaller(SCtx, State); + /// Consume the information on the no-store stack frame in order to + /// either emit a note or suppress the report enirely. + /// \return Diagnostics piece for region not modified in the current function, + /// if it decides to emit one. + std::shared_ptr<PathDiagnosticPiece> + maybeEmitNote(BugReport &R, const CallEvent &Call, const ExplodedNode *N, + const RegionVector &FieldChain, const MemRegion *MatchedRegion, + StringRef FirstElement, bool FirstIsReferenceType, + unsigned IndirectionLevel); - if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin())) - return nullptr; + /// Pretty-print region \p MatchedRegion to \p os. + /// \return Whether printing succeeded. + bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType, + const MemRegion *MatchedRegion, + const RegionVector &FieldChain, + int IndirectionLevel, + llvm::raw_svector_ostream &os); - // Region of interest corresponds to an IVar, exiting a method - // which could have written into that IVar, but did not. - if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) { - if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) { - const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion(); - if (RegionOfInterest->isSubRegionOf(SelfRegion) && - potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), - IvarR->getDecl())) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion, - "self", /*FirstIsReferenceType=*/false, - 1); - } - } + /// Print first item in the chain, return new separator. + static StringRef prettyPrintFirstElement(StringRef FirstElement, + bool MoreItemsExpected, + int IndirectionLevel, + llvm::raw_svector_ostream &os); +}; - if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { - const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); - if (RegionOfInterest->isSubRegionOf(ThisR) - && !CCall->getDecl()->isImplicit()) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR, - "this", - /*FirstIsReferenceType=*/false, 1); +} // end of anonymous namespace - // Do not generate diagnostics for not modified parameters in - // constructors. - return nullptr; - } +/// \return Whether the method declaration \p Parent +/// syntactically has a binary operation writing into the ivar \p Ivar. +static bool potentiallyWritesIntoIvar(const Decl *Parent, + const ObjCIvarDecl *Ivar) { + using namespace ast_matchers; + const char *IvarBind = "Ivar"; + if (!Parent || !Parent->hasBody()) + return false; + StatementMatcher WriteIntoIvarM = binaryOperator( + hasOperatorName("="), + hasLHS(ignoringParenImpCasts( + objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind)))); + StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); + auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext()); + for (BoundNodes &Match : Matches) { + auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind); + if (IvarRef->isFreeIvar()) + return true; - ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call); - for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) { - const ParmVarDecl *PVD = parameters[I]; - SVal S = Call->getArgSVal(I); - bool ParamIsReferenceType = PVD->getType()->isReferenceType(); - std::string ParamName = PVD->getNameAsString(); - - int IndirectionLevel = 1; - QualType T = PVD->getType(); - while (const MemRegion *R = S.getAsRegion()) { - if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T)) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R, - ParamName, ParamIsReferenceType, - IndirectionLevel); - - QualType PT = T->getPointeeType(); - if (PT.isNull() || PT->isVoidType()) break; - - if (const RecordDecl *RD = PT->getAsRecordDecl()) - if (auto P = findRegionOfInterestInRecord(RD, State, R)) - return notModifiedDiagnostics( - Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName, - ParamIsReferenceType, IndirectionLevel); - - S = State->getSVal(R, PT); - T = PT; - IndirectionLevel++; - } - } + const Expr *Base = IvarRef->getBase(); + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) + Base = ICE->getSubExpr(); - return nullptr; + if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) + if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) + if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf) + return true; + + return false; } + return false; +} -private: - /// Attempts to find the region of interest in a given CXX decl, - /// by either following the base classes or fields. - /// Dereferences fields up to a given recursion limit. - /// Note that \p Vec is passed by value, leading to quadratic copying cost, - /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. - /// \return A chain fields leading to the region of interest or None. - const Optional<RegionVector> - findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, - const MemRegion *R, - const RegionVector &Vec = {}, - int depth = 0) { +/// Get parameters associated with runtime definition in order +/// to get the correct parameter name. +static ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) { + // Use runtime definition, if available. + RuntimeDefinition RD = Call->getRuntimeDefinition(); + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl())) + return FD->parameters(); + if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl())) + return MD->parameters(); + + return Call->parameters(); +} - if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. +/// \return whether \p Ty points to a const type, or is a const reference. +static bool isPointerToConst(QualType Ty) { + return !Ty->getPointeeType().isNull() && + Ty->getPointeeType().getCanonicalType().isConstQualified(); +} + +/// Attempts to find the region of interest in a given CXX decl, +/// by either following the base classes or fields. +/// Dereferences fields up to a given recursion limit. +/// Note that \p Vec is passed by value, leading to quadratic copying cost, +/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. +/// \return A chain fields leading to the region of interest or None. +const Optional<NoStoreFuncVisitor::RegionVector> +NoStoreFuncVisitor::findRegionOfInterestInRecord( + const RecordDecl *RD, ProgramStateRef State, const MemRegion *R, + const NoStoreFuncVisitor::RegionVector &Vec /* = {} */, + int depth /* = 0 */) { + + if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. + return None; + + if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) + if (!RDX->hasDefinition()) return None; - if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) - if (!RDX->hasDefinition()) - return None; - - // Recursively examine the base classes. - // Note that following base classes does not increase the recursion depth. - if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) - for (const auto II : RDX->bases()) - if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) - if (auto Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth)) - return Out; - - for (const FieldDecl *I : RD->fields()) { - QualType FT = I->getType(); - const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R)); - const SVal V = State->getSVal(FR); - const MemRegion *VR = V.getAsRegion(); - - RegionVector VecF = Vec; - VecF.push_back(FR); - - if (RegionOfInterest == VR) - return VecF; - - if (const RecordDecl *RRD = FT->getAsRecordDecl()) - if (auto Out = - findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1)) + // Recursively examine the base classes. + // Note that following base classes does not increase the recursion depth. + if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) + for (const auto II : RDX->bases()) + if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) + if (Optional<RegionVector> Out = + findRegionOfInterestInRecord(RRD, State, R, Vec, depth)) return Out; - QualType PT = FT->getPointeeType(); - if (PT.isNull() || PT->isVoidType() || !VR) continue; + for (const FieldDecl *I : RD->fields()) { + QualType FT = I->getType(); + const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R)); + const SVal V = State->getSVal(FR); + const MemRegion *VR = V.getAsRegion(); - if (const RecordDecl *RRD = PT->getAsRecordDecl()) - if (auto Out = - findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1)) - return Out; + RegionVector VecF = Vec; + VecF.push_back(FR); - } + if (RegionOfInterest == VR) + return VecF; - return None; + if (const RecordDecl *RRD = FT->getAsRecordDecl()) + if (auto Out = + findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1)) + return Out; + + QualType PT = FT->getPointeeType(); + if (PT.isNull() || PT->isVoidType() || !VR) + continue; + + if (const RecordDecl *RRD = PT->getAsRecordDecl()) + if (Optional<RegionVector> Out = + findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1)) + return Out; } - /// \return Whether the method declaration \p Parent - /// syntactically has a binary operation writing into the ivar \p Ivar. - bool potentiallyWritesIntoIvar(const Decl *Parent, - const ObjCIvarDecl *Ivar) { - using namespace ast_matchers; - const char * IvarBind = "Ivar"; - if (!Parent || !Parent->hasBody()) - return false; - StatementMatcher WriteIntoIvarM = binaryOperator( - hasOperatorName("="), - hasLHS(ignoringParenImpCasts( - objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind)))); - StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); - auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext()); - for (BoundNodes &Match : Matches) { - auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind); - if (IvarRef->isFreeIvar()) - return true; + return None; +} + +std::shared_ptr<PathDiagnosticPiece> +NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR, + BugReport &R) { - const Expr *Base = IvarRef->getBase(); - if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) - Base = ICE->getSubExpr(); + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getStackFrame(); + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); - if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) - if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) - if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf) - return true; + // No diagnostic if region was modified inside the frame. + if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N)) + return nullptr; - return false; + CallEventRef<> Call = + BR.getStateManager().getCallEventManager().getCaller(SCtx, State); + + // Region of interest corresponds to an IVar, exiting a method + // which could have written into that IVar, but did not. + if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) { + if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) { + const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(SelfRegion) && + potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), + IvarR->getDecl())) + return maybeEmitNote(R, *Call, N, {}, SelfRegion, "self", + /*FirstIsReferenceType=*/false, 1); } - return false; } - /// Check and lazily calculate whether the region of interest is - /// modified in the stack frame to which \p N belongs. - /// The calculation is cached in FramesModifyingRegion. - bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) { - const LocationContext *Ctx = N->getLocationContext(); - const StackFrameContext *SCtx = Ctx->getStackFrame(); - if (!FramesModifyingCalculated.count(SCtx)) - findModifyingFrames(N); - return FramesModifyingRegion.count(SCtx); + if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { + const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(ThisR) && + !CCall->getDecl()->isImplicit()) + return maybeEmitNote(R, *Call, N, {}, ThisR, "this", + /*FirstIsReferenceType=*/false, 1); + + // Do not generate diagnostics for not modified parameters in + // constructors. + return nullptr; } + ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call); + for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) { + const ParmVarDecl *PVD = parameters[I]; + SVal V = Call->getArgSVal(I); + bool ParamIsReferenceType = PVD->getType()->isReferenceType(); + std::string ParamName = PVD->getNameAsString(); - /// Write to \c FramesModifyingRegion all stack frames along - /// the path in the current stack frame which modify \c RegionOfInterest. - void findModifyingFrames(const ExplodedNode *N) { - assert(N->getLocationAs<CallExitBegin>()); - ProgramStateRef LastReturnState = N->getState(); - SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); - const LocationContext *Ctx = N->getLocationContext(); - const StackFrameContext *OriginalSCtx = Ctx->getStackFrame(); + int IndirectionLevel = 1; + QualType T = PVD->getType(); + while (const MemRegion *MR = V.getAsRegion()) { + if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T)) + return maybeEmitNote(R, *Call, N, {}, MR, ParamName, + ParamIsReferenceType, IndirectionLevel); - do { - ProgramStateRef State = N->getState(); - auto CallExitLoc = N->getLocationAs<CallExitBegin>(); - if (CallExitLoc) { - LastReturnState = State; - ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); - } - - FramesModifyingCalculated.insert( - N->getLocationContext()->getStackFrame()); - - if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) { - const StackFrameContext *SCtx = N->getStackFrame(); - while (!SCtx->inTopFrame()) { - auto p = FramesModifyingRegion.insert(SCtx); - if (!p.second) - break; // Frame and all its parents already inserted. - SCtx = SCtx->getParent()->getStackFrame(); - } - } + QualType PT = T->getPointeeType(); + if (PT.isNull() || PT->isVoidType()) + break; - // Stop calculation at the call to the current function. - if (auto CE = N->getLocationAs<CallEnter>()) - if (CE->getCalleeContext() == OriginalSCtx) - break; + if (const RecordDecl *RD = PT->getAsRecordDecl()) + if (Optional<RegionVector> P = + findRegionOfInterestInRecord(RD, State, MR)) + return maybeEmitNote(R, *Call, N, *P, RegionOfInterest, ParamName, + ParamIsReferenceType, IndirectionLevel); - N = N->getFirstPred(); - } while (N); + V = State->getSVal(MR, PT); + T = PT; + IndirectionLevel++; + } } - /// Get parameters associated with runtime definition in order - /// to get the correct parameter name. - ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) { - // Use runtime definition, if available. - RuntimeDefinition RD = Call->getRuntimeDefinition(); - if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl())) - return FD->parameters(); - if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl())) - return MD->parameters(); + return nullptr; +} - return Call->parameters(); - } +void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) { + assert(N->getLocationAs<CallExitBegin>()); + ProgramStateRef LastReturnState = N->getState(); + SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *OriginalSCtx = Ctx->getStackFrame(); - /// \return whether \p Ty points to a const type, or is a const reference. - bool isPointerToConst(QualType Ty) { - return !Ty->getPointeeType().isNull() && - Ty->getPointeeType().getCanonicalType().isConstQualified(); - } + do { + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + if (CallExitLoc) { + LastReturnState = State; + ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); + } - /// \return Diagnostics piece for region not modified in the current function. - std::shared_ptr<PathDiagnosticPiece> - notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc, - CallEventRef<> Call, const RegionVector &FieldChain, - const MemRegion *MatchedRegion, StringRef FirstElement, - bool FirstIsReferenceType, unsigned IndirectionLevel) { - - PathDiagnosticLocation L; - if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { - L = PathDiagnosticLocation::createBegin(RS, SM, Ctx); - } else { - L = PathDiagnosticLocation( - Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), - SM); + FramesModifyingCalculated.insert(N->getLocationContext()->getStackFrame()); + + if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) { + const StackFrameContext *SCtx = N->getStackFrame(); + while (!SCtx->inTopFrame()) { + auto p = FramesModifyingRegion.insert(SCtx); + if (!p.second) + break; // Frame and all its parents already inserted. + SCtx = SCtx->getParent()->getStackFrame(); + } } - SmallString<256> sbuf; - llvm::raw_svector_ostream os(sbuf); - os << "Returning without writing to '"; + // Stop calculation at the call to the current function. + if (auto CE = N->getLocationAs<CallEnter>()) + if (CE->getCalleeContext() == OriginalSCtx) + break; - // Do not generate the note if failed to pretty-print. - if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, - MatchedRegion, FieldChain, IndirectionLevel, os)) - return nullptr; + N = N->getFirstPred(); + } while (N); +} - os << "'"; - return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote( + BugReport &R, const CallEvent &Call, const ExplodedNode *N, + const RegionVector &FieldChain, const MemRegion *MatchedRegion, + StringRef FirstElement, bool FirstIsReferenceType, + unsigned IndirectionLevel) { + // Optimistically suppress uninitialized value bugs that result + // from system headers having a chance to initialize the value + // but failing to do so. It's too unlikely a system header's fault. + // It's much more likely a situation in which the function has a failure + // mode that the user decided not to check. If we want to hunt such + // omitted checks, we should provide an explicit function-specific note + // describing the precondition under which the function isn't supposed to + // initialize its out-parameter, and additionally check that such + // precondition can actually be fulfilled on the current path. + if (Call.isInSystemHeader()) { + // We make an exception for system header functions that have no branches. + // Such functions unconditionally fail to initialize the variable. + // If they call other functions that have more paths within them, + // this suppression would still apply when we visit these inner functions. + // One common example of a standard function that doesn't ever initialize + // its out parameter is operator placement new; it's up to the follow-up + // constructor (if any) to initialize the memory. + if (!N->getStackFrame()->getCFG()->isLinear()) + R.markInvalid(getTag(), nullptr); + return nullptr; } - /// Pretty-print region \p MatchedRegion to \p os. - /// \return Whether printing succeeded. - bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType, - const MemRegion *MatchedRegion, - const RegionVector &FieldChain, - int IndirectionLevel, - llvm::raw_svector_ostream &os) { + PathDiagnosticLocation L = + PathDiagnosticLocation::create(N->getLocation(), SM); + + // For now this shouldn't trigger, but once it does (as we add more + // functions to the body farm), we'll need to decide if these reports + // are worth suppressing as well. + if (!L.hasValidLocation()) + return nullptr; - if (FirstIsReferenceType) - IndirectionLevel--; + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Returning without writing to '"; - RegionVector RegionSequence; + // Do not generate the note if failed to pretty-print. + if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion, + FieldChain, IndirectionLevel, os)) + return nullptr; - // Add the regions in the reverse order, then reverse the resulting array. - assert(RegionOfInterest->isSubRegionOf(MatchedRegion)); - const MemRegion *R = RegionOfInterest; - while (R != MatchedRegion) { - RegionSequence.push_back(R); - R = cast<SubRegion>(R)->getSuperRegion(); - } - std::reverse(RegionSequence.begin(), RegionSequence.end()); - RegionSequence.append(FieldChain.begin(), FieldChain.end()); + os << "'"; + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +} - StringRef Sep; - for (const MemRegion *R : RegionSequence) { +bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement, + bool FirstIsReferenceType, + const MemRegion *MatchedRegion, + const RegionVector &FieldChain, + int IndirectionLevel, + llvm::raw_svector_ostream &os) { - // Just keep going up to the base region. - // Element regions may appear due to casts. - if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R)) - continue; + if (FirstIsReferenceType) + IndirectionLevel--; - if (Sep.empty()) - Sep = prettyPrintFirstElement(FirstElement, - /*MoreItemsExpected=*/true, - IndirectionLevel, os); + RegionVector RegionSequence; - os << Sep; + // Add the regions in the reverse order, then reverse the resulting array. + assert(RegionOfInterest->isSubRegionOf(MatchedRegion)); + const MemRegion *R = RegionOfInterest; + while (R != MatchedRegion) { + RegionSequence.push_back(R); + R = cast<SubRegion>(R)->getSuperRegion(); + } + std::reverse(RegionSequence.begin(), RegionSequence.end()); + RegionSequence.append(FieldChain.begin(), FieldChain.end()); - // Can only reasonably pretty-print DeclRegions. - if (!isa<DeclRegion>(R)) - return false; + StringRef Sep; + for (const MemRegion *R : RegionSequence) { - const auto *DR = cast<DeclRegion>(R); - Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; - DR->getDecl()->getDeclName().print(os, PP); - } + // Just keep going up to the base region. + // Element regions may appear due to casts. + if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R)) + continue; if (Sep.empty()) - prettyPrintFirstElement(FirstElement, - /*MoreItemsExpected=*/false, IndirectionLevel, - os); - return true; - } + Sep = prettyPrintFirstElement(FirstElement, + /*MoreItemsExpected=*/true, + IndirectionLevel, os); - /// Print first item in the chain, return new separator. - StringRef prettyPrintFirstElement(StringRef FirstElement, - bool MoreItemsExpected, - int IndirectionLevel, - llvm::raw_svector_ostream &os) { - StringRef Out = "."; - - if (IndirectionLevel > 0 && MoreItemsExpected) { - IndirectionLevel--; - Out = "->"; - } + os << Sep; - if (IndirectionLevel > 0 && MoreItemsExpected) - os << "("; + // Can only reasonably pretty-print DeclRegions. + if (!isa<DeclRegion>(R)) + return false; - for (int i=0; i<IndirectionLevel; i++) - os << "*"; - os << FirstElement; + const auto *DR = cast<DeclRegion>(R); + Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; + DR->getDecl()->getDeclName().print(os, PP); + } - if (IndirectionLevel > 0 && MoreItemsExpected) - os << ")"; + if (Sep.empty()) + prettyPrintFirstElement(FirstElement, + /*MoreItemsExpected=*/false, IndirectionLevel, os); + return true; +} + +StringRef NoStoreFuncVisitor::prettyPrintFirstElement( + StringRef FirstElement, bool MoreItemsExpected, int IndirectionLevel, + llvm::raw_svector_ostream &os) { + StringRef Out = "."; - return Out; + if (IndirectionLevel > 0 && MoreItemsExpected) { + IndirectionLevel--; + Out = "->"; } -}; + + if (IndirectionLevel > 0 && MoreItemsExpected) + os << "("; + + for (int i = 0; i < IndirectionLevel; i++) + os << "*"; + os << FirstElement; + + if (IndirectionLevel > 0 && MoreItemsExpected) + os << ")"; + + return Out; +} + +//===----------------------------------------------------------------------===// +// Implementation of MacroNullReturnSuppressionVisitor. +//===----------------------------------------------------------------------===// + +namespace { /// Suppress null-pointer-dereference bugs where dereferenced null was returned /// the macro. @@ -744,6 +829,10 @@ private: } }; +} // end of anonymous namespace + +namespace { + /// Emits an extra note at the return statement of an interesting stack frame. /// /// The returned value is marked as an interesting value, and if it's null, @@ -795,16 +884,38 @@ public: return; // First, find when we processed the statement. + // If we work with a 'CXXNewExpr' that is going to be purged away before + // its call take place. We would catch that purge in the last condition + // as a 'StmtPoint' so we have to bypass it. + const bool BypassCXXNewExprEval = isa<CXXNewExpr>(S); + + // This is moving forward when we enter into another context. + const StackFrameContext *CurrentSFC = Node->getStackFrame(); + do { - if (auto CEE = Node->getLocationAs<CallExitEnd>()) + // If that is satisfied we found our statement as an inlined call. + if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>()) if (CEE->getCalleeContext()->getCallSite() == S) break; - if (auto SP = Node->getLocationAs<StmtPoint>()) - if (SP->getStmt() == S) - break; + // Try to move forward to the end of the call-chain. Node = Node->getFirstPred(); - } while (Node); + if (!Node) + break; + + const StackFrameContext *PredSFC = Node->getStackFrame(); + + // If that is satisfied we found our statement. + // FIXME: This code currently bypasses the call site for the + // conservatively evaluated allocator. + if (!BypassCXXNewExprEval) + if (Optional<StmtPoint> SP = Node->getLocationAs<StmtPoint>()) + // See if we do not enter into another context. + if (SP->getStmt() == S && CurrentSFC == PredSFC) + break; + + CurrentSFC = PredSFC; + } while (Node->getStackFrame() == CurrentSFC); // Next, step over any post-statement checks. while (Node && Node->getLocation().getAs<PostStmt>()) @@ -1019,7 +1130,11 @@ public: } }; -} // namespace +} // end of anonymous namespace + +//===----------------------------------------------------------------------===// +// Implementation of FindLastStoreBRVisitor. +//===----------------------------------------------------------------------===// void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { static int tag = 0; @@ -1329,6 +1444,10 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } +//===----------------------------------------------------------------------===// +// Implementation of TrackConstraintBRVisitor. +//===----------------------------------------------------------------------===// + void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { static int tag = 0; ID.AddPointer(&tag); @@ -1401,6 +1520,10 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return nullptr; } +//===----------------------------------------------------------------------===// +// Implementation of SuppressInlineDefensiveChecksVisitor. +//===----------------------------------------------------------------------===// + SuppressInlineDefensiveChecksVisitor:: SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) : V(Value) { @@ -1473,7 +1596,7 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, return nullptr; CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap(); - CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator(); + CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminatorStmt(); } else { return nullptr; } @@ -1496,6 +1619,114 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, return nullptr; } +//===----------------------------------------------------------------------===// +// TrackControlDependencyCondBRVisitor. +//===----------------------------------------------------------------------===// + +namespace { +/// Tracks the expressions that are a control dependency of the node that was +/// supplied to the constructor. +/// For example: +/// +/// cond = 1; +/// if (cond) +/// 10 / 0; +/// +/// An error is emitted at line 3. This visitor realizes that the branch +/// on line 2 is a control dependency of line 3, and tracks it's condition via +/// trackExpressionValue(). +class TrackControlDependencyCondBRVisitor final : public BugReporterVisitor { + const ExplodedNode *Origin; + ControlDependencyCalculator ControlDeps; + llvm::SmallSet<const CFGBlock *, 32> VisitedBlocks; + +public: + TrackControlDependencyCondBRVisitor(const ExplodedNode *O) + : Origin(O), ControlDeps(&O->getCFG()) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; +}; +} // end of anonymous namespace + +static CFGBlock *GetRelevantBlock(const ExplodedNode *Node) { + if (auto SP = Node->getLocationAs<StmtPoint>()) { + const Stmt *S = SP->getStmt(); + assert(S); + + return const_cast<CFGBlock *>(Node->getLocationContext() + ->getAnalysisDeclContext()->getCFGStmtMap()->getBlock(S)); + } + + return nullptr; +} + +static std::shared_ptr<PathDiagnosticEventPiece> +constructDebugPieceForTrackedCondition(const Expr *Cond, + const ExplodedNode *N, + BugReporterContext &BRC) { + + if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE || + !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug) + return nullptr; + + std::string ConditionText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Cond->getSourceRange()), + BRC.getSourceManager(), + BRC.getASTContext().getLangOpts()); + + return std::make_shared<PathDiagnosticEventPiece>( + PathDiagnosticLocation::createBegin( + Cond, BRC.getSourceManager(), N->getLocationContext()), + (Twine() + "Tracking condition '" + ConditionText + "'").str()); +} + +std::shared_ptr<PathDiagnosticPiece> +TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) { + // We can only reason about control dependencies within the same stack frame. + if (Origin->getStackFrame() != N->getStackFrame()) + return nullptr; + + CFGBlock *NB = GetRelevantBlock(N); + + // Skip if we already inspected this block. + if (!VisitedBlocks.insert(NB).second) + return nullptr; + + CFGBlock *OriginB = GetRelevantBlock(Origin); + + // TODO: Cache CFGBlocks for each ExplodedNode. + if (!OriginB || !NB) + return nullptr; + + if (ControlDeps.isControlDependent(OriginB, NB)) { + if (const Expr *Condition = NB->getLastCondition()) { + // Keeping track of the already tracked conditions on a visitor level + // isn't sufficient, because a new visitor is created for each tracked + // expression, hence the BugReport level set. + if (BR.addTrackedCondition(N)) { + bugreporter::trackExpressionValue( + N, Condition, BR, /*EnableNullFPSuppression=*/false); + return constructDebugPieceForTrackedCondition(Condition, N, BRC); + } + } + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Implementation of trackExpressionValue. +//===----------------------------------------------------------------------===// + static const MemRegion *getLocationRegionIfReference(const Expr *E, const ExplodedNode *N) { if (const auto *DR = dyn_cast<DeclRefExpr>(E)) { @@ -1545,7 +1776,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, ProgramPoint ProgPoint = NI->getLocation(); if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { const CFGBlock *srcBlk = BE->getSrc(); - if (const Stmt *term = srcBlk->getTerminator()) { + if (const Stmt *term = srcBlk->getTerminatorStmt()) { if (term == CO) { bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst()); if (TookTrueBranch) @@ -1608,12 +1839,26 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, ProgramStateRef LVState = LVNode->getState(); + // We only track expressions if we believe that they are important. Chances + // are good that control dependencies to the tracking point are also improtant + // because of this, let's explain why we believe control reached this point. + // TODO: Shouldn't we track control dependencies of every bug location, rather + // than only tracked expressions? + if (LVState->getAnalysisManager().getAnalyzerOptions().ShouldTrackConditions) + report.addVisitor(llvm::make_unique<TrackControlDependencyCondBRVisitor>( + InputNode)); + // The message send could be nil due to the receiver being nil. // At this point in the path, the receiver should be live since we are at the // message send expr. If it is nil, start tracking it. if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression); + // Track the index if this is an array subscript. + if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner)) + trackExpressionValue( + LVNode, Arr->getIdx(), report, /*EnableNullFPSuppression*/ false); + // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (ExplodedGraph::isInterestingLValueExpr(Inner)) { @@ -1715,6 +1960,10 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, return true; } +//===----------------------------------------------------------------------===// +// Implementation of NulReceiverBRVisitor. +//===----------------------------------------------------------------------===// + const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, const ExplodedNode *N) { const auto *ME = dyn_cast<ObjCMessageExpr>(S); @@ -1765,6 +2014,10 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, return std::make_shared<PathDiagnosticEventPiece>(L, OS.str()); } +//===----------------------------------------------------------------------===// +// Implementation of FindLastStoreBRVisitor. +//===----------------------------------------------------------------------===// + // Registers every VarDecl inside a Stmt with a last store visitor. void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, const Stmt *S, @@ -1825,39 +2078,36 @@ ConditionBRVisitor::VisitNode(const ExplodedNode *N, std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { - ProgramPoint progPoint = N->getLocation(); - ProgramStateRef CurrentState = N->getState(); - ProgramStateRef PrevState = N->getFirstPred()->getState(); - - // Compare the GDMs of the state, because that is where constraints - // are managed. Note that ensure that we only look at nodes that - // were generated by the analyzer engine proper, not checkers. - if (CurrentState->getGDM().getRoot() == - PrevState->getGDM().getRoot()) - return nullptr; + ProgramPoint ProgPoint = N->getLocation(); + const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags = + ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) { - const CFGBlock *srcBlk = BE->getSrc(); - if (const Stmt *term = srcBlk->getTerminator()) - return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); + if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + const CFGBlock *SrcBlock = BE->getSrc(); + if (const Stmt *Term = SrcBlock->getTerminatorStmt()) { + // If the tag of the previous node is 'Eagerly Assume...' the current + // 'BlockEdge' has the same constraint information. We do not want to + // report the value as it is just an assumption on the predecessor node + // which will be caught in the next VisitNode() iteration as a 'PostStmt'. + const ProgramPointTag *PreviousNodeTag = + N->getFirstPred()->getLocation().getTag(); + if (PreviousNodeTag == Tags.first || PreviousNodeTag == Tags.second) + return nullptr; + + return VisitTerminator(Term, N, SrcBlock, BE->getDst(), BR, BRC); + } return nullptr; } - if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) { - const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = - ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); - - const ProgramPointTag *tag = PS->getTag(); - if (tag == tags.first) - return VisitTrueTest(cast<Expr>(PS->getStmt()), true, - BRC, BR, N); - if (tag == tags.second) - return VisitTrueTest(cast<Expr>(PS->getStmt()), false, - BRC, BR, N); + if (Optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) { + const ProgramPointTag *CurrentNodeTag = PS->getTag(); + if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second) + return nullptr; - return nullptr; + bool TookTrue = CurrentNodeTag == Tags.first; + return VisitTrueTest(cast<Expr>(PS->getStmt()), BRC, BR, N, TookTrue); } return nullptr; @@ -1903,6 +2153,8 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( break; } + Cond = Cond->IgnoreParens(); + // However, when we encounter a logical operator as a branch condition, // then the condition is actually its RHS, because LHS would be // the condition for the logical operator terminator. @@ -1914,18 +2166,30 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( assert(Cond); assert(srcBlk->succ_size() == 2); - const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; - return VisitTrueTest(Cond, tookTrue, BRC, R, N); + const bool TookTrue = *(srcBlk->succ_begin()) == dstBlk; + return VisitTrueTest(Cond, BRC, R, N, TookTrue); } std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, - BugReporterContext &BRC, BugReport &R, - const ExplodedNode *N) { +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, + BugReport &R, const ExplodedNode *N, + bool TookTrue) { + ProgramStateRef CurrentState = N->getState(); + ProgramStateRef PrevState = N->getFirstPred()->getState(); + const LocationContext *LCtx = N->getLocationContext(); + + // If the constraint information is changed between the current and the + // previous program state we assuming the newly seen constraint information. + // If we cannot evaluate the condition (and the constraints are the same) + // the analyzer has no information about the value and just assuming it. + bool IsAssuming = + !BRC.getStateManager().haveEqualConstraints(CurrentState, PrevState) || + CurrentState->getSVal(Cond, LCtx).isUnknownOrUndef(); + // These will be modified in code below, but we need to preserve the original // values in case we want to throw the generic message. const Expr *CondTmp = Cond; - bool tookTrueTmp = tookTrue; + bool TookTrueTmp = TookTrue; while (true) { CondTmp = CondTmp->IgnoreParenCasts(); @@ -1934,18 +2198,23 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, break; case Stmt::BinaryOperatorClass: if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp), - tookTrueTmp, BRC, R, N)) + BRC, R, N, TookTrueTmp, IsAssuming)) return P; break; case Stmt::DeclRefExprClass: if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp), - tookTrueTmp, BRC, R, N)) + BRC, R, N, TookTrueTmp, IsAssuming)) + return P; + break; + case Stmt::MemberExprClass: + if (auto P = VisitTrueTest(Cond, cast<MemberExpr>(CondTmp), + BRC, R, N, TookTrueTmp, IsAssuming)) return P; break; case Stmt::UnaryOperatorClass: { const auto *UO = cast<UnaryOperator>(CondTmp); if (UO->getOpcode() == UO_LNot) { - tookTrueTmp = !tookTrueTmp; + TookTrueTmp = !TookTrueTmp; CondTmp = UO->getSubExpr(); continue; } @@ -1957,13 +2226,17 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, // Condition too complex to explain? Just say something so that the user // knew we've made some path decision at this point. - const LocationContext *LCtx = N->getLocationContext(); + // If it is too complex and we know the evaluation of the condition do not + // repeat the note from 'BugReporter.cpp' + if (!IsAssuming) + return nullptr; + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); if (!Loc.isValid() || !Loc.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( - Loc, tookTrue ? GenericTrueMessage : GenericFalseMessage); + Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage); } bool ConditionBRVisitor::patternMatch(const Expr *Ex, @@ -1972,47 +2245,27 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N, - Optional<bool> &prunable) { + Optional<bool> &prunable, + bool IsSameFieldName) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); - // Use heuristics to determine if Ex is a macro expending to a literal and - // if so, use the macro's name. - SourceLocation LocStart = Ex->getBeginLoc(); - SourceLocation LocEnd = Ex->getEndLoc(); - if (LocStart.isMacroID() && LocEnd.isMacroID() && - (isa<GNUNullExpr>(Ex) || - isa<ObjCBoolLiteralExpr>(Ex) || - isa<CXXBoolLiteralExpr>(Ex) || - isa<IntegerLiteral>(Ex) || - isa<FloatingLiteral>(Ex))) { - StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart, - BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd, - BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - bool beginAndEndAreTheSameMacro = StartName.equals(EndName); - - bool partOfParentMacro = false; - if (ParentEx->getBeginLoc().isMacroID()) { - StringRef PName = Lexer::getImmediateMacroNameForDiagnostics( - ParentEx->getBeginLoc(), BRC.getSourceManager(), - BRC.getASTContext().getLangOpts()); - partOfParentMacro = PName.equals(StartName); - } - - if (beginAndEndAreTheSameMacro && !partOfParentMacro ) { - // Get the location of the macro name as written by the caller. - SourceLocation Loc = LocStart; - while (LocStart.isMacroID()) { - Loc = LocStart; - LocStart = BRC.getSourceManager().getImmediateMacroCallerLoc(LocStart); + if (isa<GNUNullExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) || + isa<CXXBoolLiteralExpr>(Ex) || isa<IntegerLiteral>(Ex) || + isa<FloatingLiteral>(Ex)) { + // Use heuristics to determine if the expression is a macro + // expanding to a literal and if so, use the macro's name. + SourceLocation BeginLoc = OriginalExpr->getBeginLoc(); + SourceLocation EndLoc = OriginalExpr->getEndLoc(); + if (BeginLoc.isMacroID() && EndLoc.isMacroID()) { + SourceManager &SM = BRC.getSourceManager(); + const LangOptions &LO = BRC.getASTContext().getLangOpts(); + if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) && + Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) { + CharSourceRange R = Lexer::getAsCharRange({BeginLoc, EndLoc}, SM, LO); + Out << Lexer::getSourceText(R, SM, LO); + return false; } - StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( - Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - - // Return the macro name. - Out << MacroName; - return false; } } @@ -2059,23 +2312,43 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, return false; } + if (const auto *ME = dyn_cast<MemberExpr>(Ex)) { + if (!IsSameFieldName) + Out << "field '" << ME->getMemberDecl()->getName() << '\''; + else + Out << '\'' + << Lexer::getSourceText( + CharSourceRange::getTokenRange(Ex->getSourceRange()), + BRC.getSourceManager(), BRC.getASTContext().getLangOpts(), 0) + << '\''; + } + return false; } -std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, - const bool tookTrue, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N) { +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( + const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC, + BugReport &R, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { bool shouldInvert = false; Optional<bool> shouldPrune; + // Check if the field name of the MemberExprs is ambiguous. Example: + // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'. + bool IsSameFieldName = false; + if (const auto *LhsME = + dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts())) + if (const auto *RhsME = + dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts())) + IsSameFieldName = LhsME->getMemberDecl()->getName() == + RhsME->getMemberDecl()->getName(); + SmallString<128> LhsString, RhsString; { llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); - const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, - BRC, R, N, shouldPrune); - const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, - BRC, R, N, shouldPrune); + const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, BRC, R, + N, shouldPrune, IsSameFieldName); + const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, BRC, R, + N, shouldPrune, IsSameFieldName); shouldInvert = !isVarLHS && isVarRHS; } @@ -2085,8 +2358,8 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, if (BinaryOperator::isAssignmentOp(Op)) { // For assignment operators, all that we care about is that the LHS // evaluates to "true" or "false". - return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue, - BRC, R, N); + return VisitConditionVariable(LhsString, BExpr->getLHS(), BRC, R, N, + TookTrue); } // For non-assignment operations, we require that we can understand @@ -2098,7 +2371,8 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, // Should we invert the strings if the LHS is not a variable name? SmallString<256> buf; llvm::raw_svector_ostream Out(buf); - Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; + Out << (IsAssuming ? "Assuming " : "") + << (shouldInvert ? RhsString : LhsString) << " is "; // Do we need to invert the opcode? if (shouldInvert) @@ -2110,7 +2384,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, case BO_GE: Op = BO_LE; break; } - if (!tookTrue) + if (!TookTrue) switch (Op) { case BO_EQ: Op = BO_NE; break; case BO_NE: Op = BO_EQ; break; @@ -2137,15 +2411,24 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, Out << (shouldInvert ? LhsString : RhsString); const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); - auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + + // Convert 'field ...' to 'Field ...' if it is a MemberExpr. + std::string Message = Out.str(); + Message[0] = toupper(Message[0]); + + // If we know the value create a pop-up note. + if (!IsAssuming) + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message); + + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message); if (shouldPrune.hasValue()) event->setPrunable(shouldPrune.getValue()); return event; } std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( - StringRef LhsString, const Expr *CondVarExpr, const bool tookTrue, - BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) { + StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC, + BugReport &report, const ExplodedNode *N, bool TookTrue) { // FIXME: If there's already a constraint tracker for this variable, // we shouldn't emit anything here (c.f. the double note in // test/Analysis/inlining/path-notes.c) @@ -2153,17 +2436,7 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( llvm::raw_svector_ostream Out(buf); Out << "Assuming " << LhsString << " is "; - QualType Ty = CondVarExpr->getType(); - - if (Ty->isPointerType()) - Out << (tookTrue ? "not null" : "null"); - else if (Ty->isObjCObjectPointerType()) - Out << (tookTrue ? "not nil" : "nil"); - else if (Ty->isBooleanType()) - Out << (tookTrue ? "true" : "false"); - else if (Ty->isIntegralOrEnumerationType()) - Out << (tookTrue ? "non-zero" : "zero"); - else + if (!printValue(CondVarExpr, Out, N, TookTrue, /*IsAssuming=*/true)) return nullptr; const LocationContext *LCtx = N->getLocationContext(); @@ -2183,34 +2456,29 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( return event; } -std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, - const bool tookTrue, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N) { - const auto *VD = dyn_cast<VarDecl>(DR->getDecl()); +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( + const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC, + BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); if (!VD) return nullptr; SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); - Out << "Assuming '" << VD->getDeclName() << "' is "; + Out << (IsAssuming ? "Assuming '" : "'") << VD->getDeclName() << "' is "; - QualType VDTy = VD->getType(); - - if (VDTy->isPointerType()) - Out << (tookTrue ? "non-null" : "null"); - else if (VDTy->isObjCObjectPointerType()) - Out << (tookTrue ? "non-nil" : "nil"); - else if (VDTy->isScalarType()) - Out << (tookTrue ? "not equal to 0" : "0"); - else + if (!printValue(DRE, Out, N, TookTrue, IsAssuming)) return nullptr; const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); - auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + // If we know the value create a pop-up note. + if (!IsAssuming) + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); + + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); const ProgramState *state = N->getState().get(); if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { if (report.isInteresting(R)) @@ -2224,6 +2492,67 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, return std::move(event); } +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( + const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, + BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << (IsAssuming ? "Assuming field '" : "Field '") + << ME->getMemberDecl()->getName() << "' is "; + + if (!printValue(ME, Out, N, TookTrue, IsAssuming)) + return nullptr; + + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + if (!Loc.isValid() || !Loc.asLocation().isValid()) + return nullptr; + + // If we know the value create a pop-up note. + if (!IsAssuming) + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); + + return std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); +} + +bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, + const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { + QualType Ty = CondVarExpr->getType(); + + if (Ty->isPointerType()) { + Out << (TookTrue ? "non-null" : "null"); + return true; + } + + if (Ty->isObjCObjectPointerType()) { + Out << (TookTrue ? "non-nil" : "nil"); + return true; + } + + if (!Ty->isIntegralOrEnumerationType()) + return false; + + Optional<const llvm::APSInt *> IntValue; + if (!IsAssuming) + IntValue = getConcreteIntegerValue(CondVarExpr, N); + + if (IsAssuming || !IntValue.hasValue()) { + if (Ty->isBooleanType()) + Out << (TookTrue ? "true" : "false"); + else + Out << (TookTrue ? "not equal to 0" : "0"); + } else { + if (Ty->isBooleanType()) + Out << (IntValue.getValue()->getBoolValue() ? "true" : "false"); + else + Out << *IntValue.getValue(); + } + + return true; +} + const char *const ConditionBRVisitor::GenericTrueMessage = "Assuming the condition is true"; const char *const ConditionBRVisitor::GenericFalseMessage = @@ -2235,6 +2564,10 @@ bool ConditionBRVisitor::isPieceMessageGeneric( Piece->getString() == GenericFalseMessage; } +//===----------------------------------------------------------------------===// +// Implementation of LikelyFalsePositiveSuppressionBRVisitor. +//===----------------------------------------------------------------------===// + void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { // Here we suppress false positives coming from system headers. This list is @@ -2317,6 +2650,10 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( } } +//===----------------------------------------------------------------------===// +// Implementation of UndefOrNullArgVisitor. +//===----------------------------------------------------------------------===// + std::shared_ptr<PathDiagnosticPiece> UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { @@ -2367,78 +2704,9 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, return nullptr; } -std::shared_ptr<PathDiagnosticPiece> -CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &) { - if (Satisfied) - return nullptr; - - const auto Edge = Succ->getLocation().getAs<BlockEdge>(); - if (!Edge.hasValue()) - return nullptr; - - auto Tag = Edge->getTag(); - if (!Tag) - return nullptr; - - if (Tag->getTagDescription() != "cplusplus.SelfAssignment") - return nullptr; - - Satisfied = true; - - const auto *Met = - dyn_cast<CXXMethodDecl>(Succ->getCodeDecl().getAsFunction()); - assert(Met && "Not a C++ method."); - assert((Met->isCopyAssignmentOperator() || Met->isMoveAssignmentOperator()) && - "Not a copy/move assignment operator."); - - const auto *LCtx = Edge->getLocationContext(); - - const auto &State = Succ->getState(); - auto &SVB = State->getStateManager().getSValBuilder(); - - const auto Param = - State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx)); - const auto This = - State->getSVal(SVB.getCXXThis(Met, LCtx->getStackFrame())); - - auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager()); - - if (!L.isValid() || !L.asLocation().isValid()) - return nullptr; - - SmallString<256> Buf; - llvm::raw_svector_ostream Out(Buf); - - Out << "Assuming " << Met->getParamDecl(0)->getName() << - ((Param == This) ? " == " : " != ") << "*this"; - - auto Piece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); - Piece->addRange(Met->getSourceRange()); - - return std::move(Piece); -} - -std::shared_ptr<PathDiagnosticPiece> -TaintBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &) { - - // Find the ExplodedNode where the taint was first introduced - if (!N->getState()->isTainted(V) || N->getFirstPred()->getState()->isTainted(V)) - return nullptr; - - const Stmt *S = PathDiagnosticLocation::getStmt(N); - if (!S) - return nullptr; - - const LocationContext *NCtx = N->getLocationContext(); - PathDiagnosticLocation L = - PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); - if (!L.isValid() || !L.asLocation().isValid()) - return nullptr; - - return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here"); -} +//===----------------------------------------------------------------------===// +// Implementation of FalsePositiveRefutationBRVisitor. +//===----------------------------------------------------------------------===// FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} @@ -2449,7 +2717,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( VisitNode(EndPathNode, BRC, BR); // Create a refutation manager - SMTSolverRef RefutationSolver = CreateZ3Solver(); + llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver(); ASTContext &Ctx = BRC.getASTContext(); // Add constraints to the solver @@ -2457,7 +2725,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( const SymbolRef Sym = I.first; auto RangeIt = I.second.begin(); - SMTExprRef Constraints = SMTConv::getRangeExpr( + llvm::SMTExprRef Constraints = SMTConv::getRangeExpr( RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(), /*InRange=*/true); while ((++RangeIt) != I.second.end()) { @@ -2504,3 +2772,33 @@ void FalsePositiveRefutationBRVisitor::Profile( static int Tag = 0; ID.AddPointer(&Tag); } + +//===----------------------------------------------------------------------===// +// Implementation of TagVisitor. +//===----------------------------------------------------------------------===// + +int NoteTag::Kind = 0; + +void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); +} + +std::shared_ptr<PathDiagnosticPiece> +TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + BugReport &R) { + ProgramPoint PP = N->getLocation(); + const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag()); + if (!T) + return nullptr; + + if (Optional<std::string> Msg = T->generateMessage(BRC, R)) { + PathDiagnosticLocation Loc = + PathDiagnosticLocation::create(PP, BRC.getSourceManager()); + auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg); + Piece->setPrunable(T->isPrunable()); + return Piece; + } + + return nullptr; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 0e7f31502e81..a5f7500e6307 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -1,9 +1,8 @@ //===- CallEvent.cpp - Wrapper for all function and method calls ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -357,20 +356,32 @@ bool CallEvent::isCalled(const CallDescription &CD) const { // FIXME: Add ObjC Message support. if (getKind() == CE_ObjCMessage) return false; + + const IdentifierInfo *II = getCalleeIdentifier(); + if (!II) + return false; + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl()); + if (!FD) + return false; + + if (CD.Flags & CDF_MaybeBuiltin) { + return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()); + } + if (!CD.IsLookupDone) { CD.IsLookupDone = true; CD.II = &getState()->getStateManager().getContext().Idents.get( CD.getFunctionName()); } - const IdentifierInfo *II = getCalleeIdentifier(); - if (!II || II != CD.II) + + if (II != CD.II) return false; - const Decl *D = getDecl(); // If CallDescription provides prefix names, use them to improve matching // accuracy. - if (CD.QualifiedName.size() > 1 && D) { - const DeclContext *Ctx = D->getDeclContext(); + if (CD.QualifiedName.size() > 1 && FD) { + const DeclContext *Ctx = FD->getDeclContext(); // See if we'll be able to match them all. size_t NumUnmatched = CD.QualifiedName.size() - 1; for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) { @@ -394,8 +405,7 @@ bool CallEvent::isCalled(const CallDescription &CD) const { return false; } - return (CD.RequiredArgs == CallDescription::NoArgRequirement || - CD.RequiredArgs == getNumArgs()); + return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()); } SVal CallEvent::getArgSVal(unsigned Index) const { @@ -756,8 +766,11 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // Does the decl that we found have an implementation? const FunctionDecl *Definition; - if (!Result->hasBody(Definition)) + if (!Result->hasBody(Definition)) { + if (!DynType.canBeASubClass()) + return AnyFunctionCall::getRuntimeDefinition(); return {}; + } // We found a definition. If we're not sure that this devirtualization is // actually what will happen at runtime, make sure to provide the region so diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp index 72bfd84b40a3..f4e6f909d764 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp @@ -1,9 +1,8 @@ //== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp index 6cf931abbddd..725ff1002e29 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -1,9 +1,8 @@ //== CheckerContext.cpp - Context info for path-sensitive checkers-----------=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index e73a22ae3981..34cdc9db699d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -1,9 +1,8 @@ //===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 688c47e984cc..27d5797b4cbc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -1,9 +1,8 @@ //===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -15,7 +14,9 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/Stmt.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -59,6 +60,15 @@ void CheckerManager::finishedCheckerRegistration() { #endif } +void CheckerManager::reportInvalidCheckerOptionValue( + const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) { + + Context.getDiagnostics() + .Report(diag::err_analyzer_checker_option_invalid_input) + << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str() + << ExpectedValueDesc; +} + //===----------------------------------------------------------------------===// // Functions for running checkers for AST traversing.. //===----------------------------------------------------------------------===// @@ -641,7 +651,6 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng) { - const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); for (const auto Pred : Src) { bool anyEvaluated = false; @@ -650,16 +659,19 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, // Check if any of the EvalCall callbacks can evaluate the call. for (const auto EvalCallChecker : EvalCallCheckers) { - ProgramPoint::Kind K = ProgramPoint::PostStmtKind; - const ProgramPoint &L = - ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(), - EvalCallChecker.Checker); + // TODO: Support the situation when the call doesn't correspond + // to any Expr. + ProgramPoint L = ProgramPoint::getProgramPoint( + cast<CallExpr>(Call.getOriginExpr()), + ProgramPoint::PostStmtKind, + Pred->getLocationContext(), + EvalCallChecker.Checker); bool evaluated = false; { // CheckerContext generates transitions(populates checkDest) on // destruction, so introduce the scope to make sure it gets properly // populated. CheckerContext C(B, Eng, Pred, L); - evaluated = EvalCallChecker(CE, C); + evaluated = EvalCallChecker(Call, C); } assert(!(evaluated && anyEvaluated) && "There are more than one checkers evaluating the call"); @@ -689,11 +701,73 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit( EndOfTranslationUnitChecker(TU, mgr, BR); } -void CheckerManager::runCheckersForPrintState(raw_ostream &Out, - ProgramStateRef State, - const char *NL, const char *Sep) { - for (const auto &CheckerTag : CheckerTags) - CheckerTag.second->printState(Out, State, NL, Sep); +void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + unsigned int Space, + bool IsDot) const { + Indent(Out, Space, IsDot) << "\"checker_messages\": "; + + // Create a temporary stream to see whether we have any message. + SmallString<1024> TempBuf; + llvm::raw_svector_ostream TempOut(TempBuf); + unsigned int InnerSpace = Space + 2; + + // Create the new-line in JSON with enough space. + SmallString<128> NewLine; + llvm::raw_svector_ostream NLOut(NewLine); + NLOut << "\", " << NL; // Inject the ending and a new line + Indent(NLOut, InnerSpace, IsDot) << "\""; // then begin the next message. + + ++Space; + bool HasMessage = false; + + // Store the last CheckerTag. + const void *LastCT = nullptr; + for (const auto &CT : CheckerTags) { + // See whether the current checker has a message. + CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); + + if (TempBuf.empty()) + continue; + + if (!HasMessage) { + Out << '[' << NL; + HasMessage = true; + } + + LastCT = &CT; + TempBuf.clear(); + } + + for (const auto &CT : CheckerTags) { + // See whether the current checker has a message. + CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); + + if (TempBuf.empty()) + continue; + + Indent(Out, Space, IsDot) + << "{ \"checker\": \"" << CT.second->getCheckName().getName() + << "\", \"messages\": [" << NL; + Indent(Out, InnerSpace, IsDot) + << '\"' << TempBuf.str().trim() << '\"' << NL; + Indent(Out, Space, IsDot) << "]}"; + + if (&CT != LastCT) + Out << ','; + Out << NL; + + TempBuf.clear(); + } + + // It is the last element of the 'program_state' so do not add a comma. + if (HasMessage) + Indent(Out, --Space, IsDot) << "]"; + else + Out << "null"; + + Out << NL; } //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp index cdae3ef0116a..54501314386a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp @@ -1,9 +1,8 @@ //=--- CommonBugCategories.cpp - Provides common issue categories -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp index ef9c44c51be4..d642c3530268 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -1,9 +1,8 @@ //===- ConstraintManager.cpp - Constraints on symbolic values. ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 196854cb09da..94cf74de8293 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -1,9 +1,8 @@ //===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -217,6 +216,25 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { LC->getDecl(), LC->getCFG()->getNumBlockIDs()); + // Display a prunable path note to the user if it's a virtual bases branch + // and we're taking the path that skips virtual base constructors. + if (L.getSrc()->getTerminator().isVirtualBaseBranch() && + L.getDst() == *L.getSrc()->succ_begin()) { + ProgramPoint P = L.withTag(getNoteTags().makeNoteTag( + [](BugReporterContext &, BugReport &) -> std::string { + // TODO: Just call out the name of the most derived class + // when we know it. + return "Virtual base initialization skipped because " + "it has already been handled by the most derived class"; + }, /*IsPrunable=*/true)); + // Perform the transition. + ExplodedNodeSet Dst; + NodeBuilder Bldr(Pred, Dst, BuilderCtx); + Pred = Bldr.generateNode(P, Pred->getState(), Pred); + if (!Pred) + return; + } + // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { assert(L.getLocationContext()->getCFG()->getExit().empty() && @@ -276,14 +294,14 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, } void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { - if (const Stmt *Term = B->getTerminator()) { + if (const Stmt *Term = B->getTerminatorStmt()) { switch (Term->getStmtClass()) { default: llvm_unreachable("Analysis for this terminator not implemented."); case Stmt::CXXBindTemporaryExprClass: HandleCleanupTemporaryBranch( - cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred); + cast<CXXBindTemporaryExpr>(Term), B, Pred); return; // Model static initializers. @@ -378,9 +396,19 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { case Stmt::WhileStmtClass: HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); return; + + case Stmt::GCCAsmStmtClass: + assert(cast<GCCAsmStmt>(Term)->isAsmGoto() && "Encountered GCCAsmStmt without labels"); + // TODO: Handle jumping to labels + return; } } + if (B->getTerminator().isVirtualBaseBranch()) { + HandleVirtualBaseBranch(B, Pred); + return; + } + assert(B->succ_size() == 1 && "Blocks with no terminator should have at most 1 successor."); @@ -440,6 +468,29 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, } } +void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B, + ExplodedNode *Pred) { + const LocationContext *LCtx = Pred->getLocationContext(); + if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>( + LCtx->getStackFrame()->getCallSite())) { + switch (CallerCtor->getConstructionKind()) { + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: { + BlockEdge Loc(B, *B->succ_begin(), LCtx); + HandleBlockEdge(Loc, Pred); + return; + } + default: + break; + } + } + + // We either don't see a parent stack frame because we're in the top frame, + // or the parent stack frame doesn't initialize our virtual bases. + BlockEdge Loc(B, *(B->succ_begin() + 1), LCtx); + HandleBlockEdge(Loc, Pred); +} + /// generateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. void CoreEngine::generateNode(const ProgramPoint &Loc, diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp index da7854df1def..79424452240d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -1,9 +1,8 @@ //===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" @@ -36,7 +36,7 @@ DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, // Otherwise, fall back to what we know about the region. if (const auto *TR = dyn_cast<TypedRegion>(Reg)) - return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) { SymbolRef Sym = SR->getSymbol(); @@ -54,27 +54,38 @@ ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, return NewState; } -void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out, - const char *NL, const char *Sep) { - bool First = true; - for (const auto &I : State->get<DynamicTypeMap>()) { - if (First) { - Out << NL << "Dynamic types of regions:" << NL; - First = false; - } - const MemRegion *MR = I.first; - const DynamicTypeInfo &DTI = I.second; - Out << MR << " : "; +void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, bool IsDot) { + Indent(Out, Space, IsDot) << "\"dynamic_types\": "; + + const DynamicTypeMapTy &DTM = State->get<DynamicTypeMap>(); + if (DTM.isEmpty()) { + Out << "null," << NL; + return; + } + + ++Space; + Out << '[' << NL; + for (DynamicTypeMapTy::iterator I = DTM.begin(); I != DTM.end(); ++I) { + const MemRegion *MR = I->first; + const DynamicTypeInfo &DTI = I->second; + Out << "{ \"region\": \"" << MR << "\", \"dyn_type\": "; if (DTI.isValid()) { - Out << DTI.getType()->getPointeeType().getAsString(); - if (DTI.canBeASubClass()) { - Out << " (or its subclass)"; - } + Out << '\"' << DTI.getType()->getPointeeType().getAsString() + << "\", \"sub_classable\": " + << (DTI.canBeASubClass() ? "true" : "false"); } else { - Out << "Invalid type info"; + Out << "null"; // Invalid type info } + Out << "}"; + + if (std::next(I) != DTM.end()) + Out << ','; Out << NL; } + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; } void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp index b45f93b6dde8..551c89b04db4 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -1,9 +1,8 @@ //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -19,6 +18,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" @@ -200,43 +200,86 @@ EnvironmentManager::removeDeadBindings(Environment Env, return NewEnv; } -void Environment::print(raw_ostream &Out, const char *NL, - const char *Sep, - const ASTContext &Context, - const LocationContext *WithLC) const { - if (ExprBindings.isEmpty()) +void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + Indent(Out, Space, IsDot) << "\"environment\": "; + + if (ExprBindings.isEmpty()) { + Out << "null," << NL; return; + } - if (!WithLC) { + ++Space; + if (!LCtx) { // Find the freshest location context. llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts; - for (auto I : *this) { + for (const auto &I : *this) { const LocationContext *LC = I.first.getLocationContext(); if (FoundContexts.count(LC) == 0) { // This context is fresher than all other contexts so far. - WithLC = LC; + LCtx = LC; for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()) FoundContexts.insert(LCI); } } } - assert(WithLC); + assert(LCtx); + + Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame() + << "\", \"items\": [" << NL; + PrintingPolicy PP = Ctx.getPrintingPolicy(); - PrintingPolicy PP = Context.getPrintingPolicy(); + LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { + // LCtx items begin + bool HasItem = false; + unsigned int InnerSpace = Space + 1; - Out << NL << "Expressions by stack frame:" << NL; - WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { - for (auto I : ExprBindings) { - if (I.first.getLocationContext() != LC) + // Store the last ExprBinding which we will print. + BindingsTy::iterator LastI = ExprBindings.end(); + for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); + ++I) { + if (I->first.getLocationContext() != LC) continue; - const Stmt *S = I.first.getStmt(); + if (!HasItem) { + HasItem = true; + Out << '[' << NL; + } + + const Stmt *S = I->first.getStmt(); + (void)S; assert(S != nullptr && "Expected non-null Stmt"); - Out << "(LC" << LC->getID() << ", S" << S->getID(Context) << ") "; - S->printPretty(Out, /*Helper=*/nullptr, PP); - Out << " : " << I.second << NL; + LastI = I; + } + + for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); + ++I) { + if (I->first.getLocationContext() != LC) + continue; + + const Stmt *S = I->first.getStmt(); + Indent(Out, InnerSpace, IsDot) + << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": "; + S->printJson(Out, nullptr, PP, /*AddQuotes=*/true); + + Out << ", \"value\": "; + I->second.printJson(Out, /*AddQuotes=*/true); + + Out << " }"; + + if (I != LastI) + Out << ','; + Out << NL; } + + if (HasItem) + Indent(Out, --InnerSpace, IsDot) << ']'; + else + Out << "null "; }); + + Indent(Out, --Space, IsDot) << "]}," << NL; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index d6bcbb96b55f..c86b1436baab 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -1,9 +1,8 @@ //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 151eef56fece..1fef5b3c1edd 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1,9 +1,8 @@ //===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -34,6 +33,7 @@ #include "clang/Analysis/ConstructionContext.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/PrettyStackTrace.h" @@ -142,21 +142,34 @@ public: return getLocationContext()->getDecl()->getASTContext(); } - void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { - OS << "(LC" << getLocationContext()->getID() << ','; - if (const Stmt *S = getItem().getStmtOrNull()) - OS << 'S' << S->getID(getASTContext()); + void printJson(llvm::raw_ostream &Out, PrinterHelper *Helper, + PrintingPolicy &PP) const { + const Stmt *S = getItem().getStmtOrNull(); + const CXXCtorInitializer *I = nullptr; + if (!S) + I = getItem().getCXXCtorInitializer(); + + if (S) + Out << "\"stmt_id\": " << S->getID(getASTContext()); else - OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext()); - OS << ',' << getItem().getKindAsString(); + Out << "\"init_id\": " << I->getID(getASTContext()); + + // Kind + Out << ", \"kind\": \"" << getItem().getKindAsString() + << "\", \"argument_index\": "; + if (getItem().getKind() == ConstructionContextItem::ArgumentKind) - OS << " #" << getItem().getIndex(); - OS << ") "; - if (const Stmt *S = getItem().getStmtOrNull()) { - S->printPretty(OS, Helper, PP); + Out << getItem().getIndex(); + else + Out << "null"; + + // Pretty-print + Out << ", \"pretty\": "; + + if (S) { + S->printJson(Out, Helper, PP, /*AddQuotes=*/true); } else { - const CXXCtorInitializer *I = getItem().getCXXCtorInitializer(); - OS << I->getAnyMember()->getNameAsString(); + Out << '\"' << I->getAnyMember()->getNameAsString() << '\"'; } } @@ -198,9 +211,13 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, mgr.getConstraintManagerCreator(), G.getAllocator(), this), SymMgr(StateMgr.getSymbolManager()), - svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + MRMgr(StateMgr.getRegionManager()), + svalBuilder(StateMgr.getSValBuilder()), + ObjCNoRet(mgr.getASTContext()), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { + VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) + { unsigned TrimInterval = mgr.options.GraphTrimInterval; if (TrimInterval != 0) { // Enable eager node reclamation when constructing the ExplodedGraph. @@ -208,10 +225,6 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, } } -ExprEngine::~ExprEngine() { - BR.FlushReports(); -} - //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// @@ -538,36 +551,73 @@ ExprEngine::processRegionChanges(ProgramStateRef state, LCtx, Call); } -static void printObjectsUnderConstructionForContext(raw_ostream &Out, - ProgramStateRef State, - const char *NL, - const LocationContext *LC) { +static void +printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, const LocationContext *LCtx, + unsigned int Space = 0, bool IsDot = false) { PrintingPolicy PP = - LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); - for (auto I : State->get<ObjectsUnderConstruction>()) { - ConstructedObjectKey Key = I.first; + LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + + ++Space; + bool HasItem = false; + + // Store the last key. + const ConstructedObjectKey *LastKey = nullptr; + for (const auto &I : State->get<ObjectsUnderConstruction>()) { + const ConstructedObjectKey &Key = I.first; + if (Key.getLocationContext() != LCtx) + continue; + + if (!HasItem) { + Out << "[" << NL; + HasItem = true; + } + + LastKey = &Key; + } + + for (const auto &I : State->get<ObjectsUnderConstruction>()) { + const ConstructedObjectKey &Key = I.first; SVal Value = I.second; - if (Key.getLocationContext() != LC) + if (Key.getLocationContext() != LCtx) continue; - Key.print(Out, nullptr, PP); - Out << " : " << Value << NL; + + Indent(Out, Space, IsDot) << "{ "; + Key.printJson(Out, nullptr, PP); + Out << ", \"value\": \"" << Value << "\" }"; + + if (&Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; } } -void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep, - const LocationContext *LCtx) { - if (LCtx) { - if (!State->get<ObjectsUnderConstruction>().isEmpty()) { - Out << Sep << "Objects under construction:" << NL; +void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + Indent(Out, Space, IsDot) << "\"constructing_objects\": "; - LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { - printObjectsUnderConstructionForContext(Out, State, NL, LC); - }); - } + if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) { + ++Space; + Out << '[' << NL; + LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { + printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot); + }); + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects". + } else { + Out << "null," << NL; } - getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); + getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space, + IsDot); } void ExprEngine::processEndWorklist() { @@ -1338,6 +1388,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: @@ -1517,7 +1568,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ProgramStateRef NewState = createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0)); if (NewState != State) { - Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr, + Pred = Bldr.generateNode(OCE, Pred, NewState, /*tag=*/nullptr, ProgramPoint::PreStmtKind); // Did we cache out? if (!Pred) @@ -1636,6 +1687,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: + case Stmt::BuiltinBitCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const auto *C = cast<CastExpr>(S); @@ -1858,7 +1910,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // other constraints) then consider completely unrolling it. if(AMgr.options.ShouldUnrollLoops) { unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; - const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); if (Term) { ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(), Pred, maxBlockVisitOnPath); @@ -1879,7 +1931,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, unsigned int BlockCount = nodeBuilder.getContext().blockCount(); if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && AMgr.options.ShouldWidenLoops) { - const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); if (!(Term && (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term)))) return; @@ -2004,8 +2056,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition, if (!BO || !BO->isLogicalOp()) return Condition; - assert(!B->getTerminator().isTemporaryDtorsBranch() && - "Temporary destructor branches handled by processBindTemporary."); + assert(B->getTerminator().isStmtBranch() && + "Other kinds of branches are handled separately!"); // For logical operations, we still have the case where some branches // use the traditional "merge" approach and others sink the branch @@ -2258,7 +2310,6 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, Pred->getStackFrame()->getParent())); PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); - StateMgr.EndPath(Pred->getState()); ExplodedNodeSet Dst; if (Pred->getLocationContext()->inTopFrame()) { @@ -2620,43 +2671,39 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); } -// A value escapes in three possible cases: +// A value escapes in four possible cases: // (1) We are binding to something that is not a memory region. -// (2) We are binding to a MemrRegion that does not have stack storage. -// (3) We are binding to a MemRegion with stack storage that the store +// (2) We are binding to a MemRegion that does not have stack storage. +// (3) We are binding to a top-level parameter region with a non-trivial +// destructor. We won't see the destructor during analysis, but it's there. +// (4) We are binding to a MemRegion with stack storage that the store // does not understand. -ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, - SVal Loc, - SVal Val, - const LocationContext *LCtx) { - // Are we storing to something that causes the value to "escape"? - bool escapes = true; - - // TODO: Move to StoreManager. - if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) { - escapes = !regionLoc->getRegion()->hasStackStorage(); - - if (!escapes) { - // To test (3), generate a new state with the binding added. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - // Do this only if we know that the store is not supposed to generate the - // same state. - SVal StoredVal = State->getSVal(regionLoc->getRegion()); - if (StoredVal != Val) - escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return State; +ProgramStateRef +ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, + SVal Val, const LocationContext *LCtx) { + + // Cases (1) and (2). + const MemRegion *MR = Loc.getAsRegion(); + if (!MR || !MR->hasStackStorage()) + return escapeValue(State, Val, PSK_EscapeOnBind); + + // Case (3). + if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion())) + if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame()) + if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl()) + if (!RD->hasTrivialDestructor()) + return escapeValue(State, Val, PSK_EscapeOnBind); + + // Case (4): in order to test that, generate a new state with the binding + // added. If it is the same state, then it escapes (since the store cannot + // represent the binding). + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = State->getSVal(MR); + if (StoredVal != Val) + if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx))) + return escapeValue(State, Val, PSK_EscapeOnBind); - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - State = escapeValue(State, Val, PSK_EscapeOnBind); return State; } @@ -2959,7 +3006,8 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { for (const auto &EQ : EQClasses) { for (const BugReport &Report : EQ) { - if (Report.getErrorNode() == N) + if (Report.getErrorNode()->getState() == N->getState() && + Report.getErrorNode()->getLocation() == N->getLocation()) return true; } } @@ -2995,57 +3043,63 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { return false; } - static std::string getNodeAttributes(const ExplodedNode *N, - ExplodedGraph *) { - SmallVector<StringRef, 10> Out; - auto Noop = [](const ExplodedNode*){}; - if (traverseHiddenNodes(N, Noop, Noop, &nodeHasBugReport)) { - Out.push_back("style=filled"); - Out.push_back("fillcolor=red"); - } - - if (traverseHiddenNodes(N, Noop, Noop, - [](const ExplodedNode *C) { return C->isSink(); })) - Out.push_back("color=blue"); - return llvm::join(Out, ","); - } - static bool isNodeHidden(const ExplodedNode *N) { return N->isTrivial(); } static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){ - std::string sbuf; - llvm::raw_string_ostream Out(sbuf); + std::string Buf; + llvm::raw_string_ostream Out(Buf); + const bool IsDot = true; + const unsigned int Space = 1; ProgramStateRef State = N->getState(); + auto Noop = [](const ExplodedNode*){}; + bool HasReport = traverseHiddenNodes( + N, Noop, Noop, &nodeHasBugReport); + bool IsSink = traverseHiddenNodes( + N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); }); + + Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \"" + << (const void *)N << "\", \"state_id\": " << State->getID() + << ", \"has_report\": " << (HasReport ? "true" : "false") + << ", \"is_sink\": " << (IsSink ? "true" : "false") + << ",\\l"; + + Indent(Out, Space, IsDot) << "\"program_points\": [\\l"; + // Dump program point for all the previously skipped nodes. traverseHiddenNodes( N, [&](const ExplodedNode *OtherNode) { - OtherNode->getLocation().print(/*CR=*/"\\l", Out); + Indent(Out, Space + 1, IsDot) << "{ "; + OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); + Out << ", \"tag\": "; if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) - Out << "\\lTag:" << Tag->getTagDescription(); - if (N->isSink()) - Out << "\\lNode is sink\\l"; - if (nodeHasBugReport(N)) - Out << "\\lBug report attached\\l"; + Out << '\"' << Tag->getTagDescription() << "\" }"; + else + Out << "null }"; }, - [&](const ExplodedNode *) { Out << "\\l--------\\l"; }, + // Adds a comma and a new-line between each program point. + [&](const ExplodedNode *) { Out << ",\\l"; }, [&](const ExplodedNode *) { return false; }); - Out << "\\l\\|"; - - Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G) - << " <" << (const void *)N << ">\\|"; + Out << "\\l"; // Adds a new-line to the last program point. + Indent(Out, Space, IsDot) << "],\\l"; bool SameAsAllPredecessors = std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) { return P->getState() == State; }); - if (!SameAsAllPredecessors) - State->printDOT(Out, N->getLocationContext()); + + if (!SameAsAllPredecessors) { + State->printDOT(Out, N->getLocationContext(), Space); + } else { + Indent(Out, Space, IsDot) << "\"program_state\": null"; + } + + Out << "\\l}\\l"; return Out.str(); } }; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index b980628878e9..f436650fbdd9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -101,6 +100,10 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); if (!Result.isUnknown()) { state = state->BindExpr(B, LCtx, Result); + } else { + // If we cannot evaluate the operation escape the operands. + state = escapeValue(state, LeftV, PSK_EscapeOther); + state = escapeValue(state, RightV, PSK_EscapeOther); } Bldr.generateNode(B, *it, state); @@ -377,9 +380,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: + case CK_LValueToRValueBitCast: case CK_AddressSpaceConversion: case CK_BooleanToSignedIntegral: - case CK_NullToPointer: case CK_IntegralToPointer: case CK_PointerToIntegral: { SVal V = state->getSVal(Ex, LCtx); @@ -416,7 +419,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_IntToOCLSampler: case CK_LValueBitCast: case CK_FixedPointCast: - case CK_FixedPointToBoolean: { + case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; @@ -502,6 +507,12 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + case CK_NullToPointer: { + SVal V = svalBuilder.makeNull(); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } case CK_NullToMemberPointer: { SVal V = svalBuilder.getMemberPointer(nullptr); state = state->BindExpr(CastE, LCtx, V); @@ -626,6 +637,21 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + // This method acts upon CFG elements for logical operators && and || + // and attaches the value (true or false) to them as expressions. + // It doesn't produce any state splits. + // If we made it that far, we're past the point when we modeled the short + // circuit. It means that we should have precise knowledge about whether + // we've short-circuited. If we did, we already know the value we need to + // bind. If we didn't, the value of the RHS (casted to the boolean type) + // is the answer. + // Currently this method tries to figure out whether we've short-circuited + // by looking at the ExplodedGraph. This method is imperfect because there + // could inevitably have been merges that would have resulted in multiple + // potential path traversal histories. We bail out when we fail. + // Due to this ambiguity, a more reliable solution would have been to + // track the short circuit operation history path-sensitively until + // we evaluate the respective logical operator. assert(B->getOpcode() == BO_LAnd || B->getOpcode() == BO_LOr); @@ -647,10 +673,20 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ProgramPoint P = N->getLocation(); assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>()); (void) P; - assert(N->pred_size() == 1); + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } N = *N->pred_begin(); } - assert(N->pred_size() == 1); + + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } + N = *N->pred_begin(); BlockEdge BE = N->getLocation().castAs<BlockEdge>(); SVal X; @@ -703,7 +739,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, QualType T = getContext().getCanonicalType(IE->getType()); unsigned NumInitElements = IE->getNumInits(); - if (!IE->isGLValue() && + if (!IE->isGLValue() && !IE->isTransparent() && (T->isArrayType() || T->isRecordType() || T->isVectorType() || T->isAnyComplexType())) { llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 6445b9df5a58..1cbd09ea5793 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1,9 +1,8 @@ //===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -197,6 +196,12 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( // able to find construction context at all. break; } + if (isa<BlockInvocationContext>(CallerLCtx)) { + // Unwrap block invocation contexts. They're mostly part of + // the current stack frame. + CallerLCtx = CallerLCtx->getParent(); + assert(!isa<BlockInvocationContext>(CallerLCtx)); + } return prepareForObjectConstruction( cast<Expr>(SFC->getCallSite()), State, CallerLCtx, RTC->getConstructionContext(), CallOpts); @@ -423,25 +428,20 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts); break; } - case CXXConstructExpr::CK_VirtualBase: + case CXXConstructExpr::CK_VirtualBase: { // Make sure we are not calling virtual base class initializers twice. // Only the most-derived object should initialize virtual base classes. - if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) { - const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer); - if (OuterCtor) { - switch (OuterCtor->getConstructionKind()) { - case CXXConstructExpr::CK_NonVirtualBase: - case CXXConstructExpr::CK_VirtualBase: - // Bail out! - destNodes.Add(Pred); - return; - case CXXConstructExpr::CK_Complete: - case CXXConstructExpr::CK_Delegating: - break; - } - } - } + const auto *OuterCtor = dyn_cast_or_null<CXXConstructExpr>( + LCtx->getStackFrame()->getCallSite()); + assert( + (!OuterCtor || + OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Complete || + OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Delegating) && + ("This virtual base should have already been initialized by " + "the most derived class!")); + (void)OuterCtor; LLVM_FALLTHROUGH; + } case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would // be initialized as aggregates without a constructor call, so we may have @@ -604,6 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, ExplodedNode *Pred, ExplodedNodeSet &Dst, const EvalCallOptions &CallOpts) { + assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -611,6 +612,19 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); + // FIXME: There should always be a Decl, otherwise the destructor call + // shouldn't have been added to the CFG in the first place. + if (!DtorDecl) { + // Skip the invalid destructor. We cannot simply return because + // it would interrupt the analysis instead. + static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); + // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway. + PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXDestructorCall> Call = CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); @@ -629,7 +643,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); - ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 758195d8d911..b935e3afe34b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -328,30 +327,30 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { ExplodedNodeSet DstPostPostCallCallback; getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, CEENode, *UpdatedCall, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); for (auto I : DstPostPostCallCallback) { getCheckerManager().runCheckersForNewAllocator( CNE, *getObjectUnderConstruction(I->getState(), CNE, calleeCtx->getParent()), DstPostCall, I, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); } } else { getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *UpdatedCall, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); } ExplodedNodeSet Dst; if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); } else if (CE && !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr. AMgr.getAnalyzerOptions().MayInlineCXXAllocator)) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, - *this, /*WasInlined=*/true); + *this, /*wasInlined=*/true); } else { Dst.insert(DstPostCall); } @@ -365,6 +364,26 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { } } +bool ExprEngine::isSmall(AnalysisDeclContext *ADC) const { + // When there are no branches in the function, it means that there's no + // exponential complexity introduced by inlining such function. + // Such functions also don't trigger various fundamental problems + // with our inlining mechanism, such as the problem of + // inlined defensive checks. Hence isLinear(). + const CFG *Cfg = ADC->getCFG(); + return Cfg->isLinear() || Cfg->size() <= AMgr.options.AlwaysInlineSize; +} + +bool ExprEngine::isLarge(AnalysisDeclContext *ADC) const { + const CFG *Cfg = ADC->getCFG(); + return Cfg->size() >= AMgr.options.MinCFGSizeTreatFunctionsAsLarge; +} + +bool ExprEngine::isHuge(AnalysisDeclContext *ADC) const { + const CFG *Cfg = ADC->getCFG(); + return Cfg->getNumBlockIDs() > AMgr.options.MaxInlinableSize; +} + void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, bool &IsRecursive, unsigned &StackDepth) { IsRecursive = false; @@ -385,8 +404,7 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, // Do not count the small functions when determining the stack depth. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI); - const CFG *CalleeCFG = CalleeADC->getCFG(); - if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize) + if (!isSmall(CalleeADC)) ++StackDepth; } LCtx = LCtx->getParent(); @@ -616,12 +634,19 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, std::tie(State, Target) = prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx, RTC->getConstructionContext(), CallOpts); - assert(Target.getAsRegion()); - // Invalidate the region so that it didn't look uninitialized. Don't notify - // the checkers. - State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx, - /* CausedByPointerEscape=*/false, nullptr, - &Call, nullptr); + const MemRegion *TargetR = Target.getAsRegion(); + assert(TargetR); + // Invalidate the region so that it didn't look uninitialized. If this is + // a field or element constructor, we do not want to invalidate + // the whole structure. Pointer escape is meaningless because + // the structure is a product of conservative evaluation + // and therefore contains nothing interesting at this point. + RegionAndSymbolInvalidationTraits ITraits; + ITraits.setTrait(TargetR, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + State = State->invalidateRegions(TargetR, E, Count, LCtx, + /* CausesPointerEscape=*/false, nullptr, + &Call, &ITraits); R = State->getSVal(Target.castAs<Loc>(), E->getType()); } else { @@ -833,8 +858,7 @@ static bool isCXXSharedPtrDtor(const FunctionDecl *FD) { /// This checks static properties of the function, such as its signature and /// CFG, to determine whether the analyzer should ever consider inlining it, /// in any context. -static bool mayInlineDecl(AnalysisManager &AMgr, - AnalysisDeclContext *CalleeADC) { +bool ExprEngine::mayInlineDecl(AnalysisDeclContext *CalleeADC) const { AnalyzerOptions &Opts = AMgr.getAnalyzerOptions(); // FIXME: Do not inline variadic calls. if (CallEvent::isVariadic(CalleeADC->getDecl())) @@ -879,7 +903,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr, return false; // Do not inline large functions. - if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize) + if (isHuge(CalleeADC)) return false; // It is possible that the live variables analysis cannot be @@ -919,7 +943,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, } else { // We haven't actually checked the static properties of this function yet. // Do that now, and record our decision in the function summaries. - if (mayInlineDecl(getAnalysisManager(), CalleeADC)) { + if (mayInlineDecl(CalleeADC)) { Engine.FunctionSummaries->markMayInline(D); } else { Engine.FunctionSummaries->markShouldNotInline(D); @@ -940,29 +964,23 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, return false; } - const CFG *CalleeCFG = CalleeADC->getCFG(); - // Do not inline if recursive or we've reached max stack frame count. bool IsRecursive = false; unsigned StackDepth = 0; examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); if ((StackDepth >= Opts.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize) - || IsRecursive)) + (!isSmall(CalleeADC) || IsRecursive)) return false; // Do not inline large functions too many times. if ((Engine.FunctionSummaries->getNumTimesInlined(D) > Opts.MaxTimesInlineLarge) && - CalleeCFG->getNumBlockIDs() >= - Opts.MinCFGSizeTreatFunctionsAsLarge) { + isLarge(CalleeADC)) { NumReachedInlineCountMax++; return false; } - if (HowToInline == Inline_Minimal && - (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize - || IsRecursive)) + if (HowToInline == Inline_Minimal && (!isSmall(CalleeADC) || IsRecursive)) return false; return true; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 6b8402f621e0..eb9a0be2e5d6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp index 94edd84d15d2..2b9a45133bba 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -1,9 +1,8 @@ //===- FunctionSummary.cpp - Stores summaries of functions. ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index fc82f1176942..64c42699fcf3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -1,9 +1,8 @@ //===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -91,8 +90,9 @@ public: const PathDiagnosticMacroPiece& P, unsigned num); - void HandlePiece(Rewriter& R, FileID BugFileID, - const PathDiagnosticPiece& P, unsigned num, unsigned max); + void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P, + const std::vector<SourceRange> &PopUpRanges, unsigned num, + unsigned max); void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range, const char *HighlightStart = "<span class=\"mrange\">", @@ -274,7 +274,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, std::vector<FileID> FileIDs; for (auto I : path) { FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID(); - if (std::find(FileIDs.begin(), FileIDs.end(), FID) != FileIDs.end()) + if (llvm::is_contained(FileIDs, FID)) continue; FileIDs.push_back(FID); @@ -606,6 +606,53 @@ window.addEventListener("keydown", function (event) { )<<<"; } +static void +HandlePopUpPieceStartTag(Rewriter &R, + const std::vector<SourceRange> &PopUpRanges) { + for (const auto &Range : PopUpRanges) { + html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", + "<table class='variable_popup'><tbody>", + /*IsTokenRange=*/false); + } +} + +static void HandlePopUpPieceEndTag(Rewriter &R, + const PathDiagnosticPopUpPiece &Piece, + std::vector<SourceRange> &PopUpRanges, + unsigned int LastReportedPieceIndex, + unsigned int PopUpPieceIndex) { + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + SourceRange Range(Piece.getLocation().asRange()); + + // Write out the path indices with a right arrow and the message as a row. + Out << "<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>" + << LastReportedPieceIndex; + + // Also annotate the state transition with extra indices. + Out << '.' << PopUpPieceIndex; + + Out << "</div></td><td>" << Piece.getString() << "</td></tr>"; + + // If no report made at this range mark the variable and add the end tags. + if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) == + PopUpRanges.end()) { + // Store that we create a report at this range. + PopUpRanges.push_back(Range); + + Out << "</tbody></table></span>"; + html::HighlightRange(R, Range.getBegin(), Range.getEnd(), + "<span class='variable'>", Buf.c_str(), + /*IsTokenRange=*/false); + + // Otherwise inject just the new row at the end of the range. + } else { + html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(), + /*IsTokenRange=*/false); + } +} + void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces& path, FileID FID) { // Process the path. @@ -616,39 +663,80 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, [](const std::shared_ptr<PathDiagnosticPiece> &p) { return isa<PathDiagnosticNotePiece>(*p); }); + unsigned PopUpPieceCount = + std::count_if(path.begin(), path.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &p) { + return isa<PathDiagnosticPopUpPiece>(*p); + }); - unsigned TotalRegularPieces = TotalPieces - TotalNotePieces; + unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount; unsigned NumRegularPieces = TotalRegularPieces; unsigned NumNotePieces = TotalNotePieces; + // Stores the count of the regular piece indices. + std::map<int, int> IndexMap; + // Stores the different ranges where we have reported something. + std::vector<SourceRange> PopUpRanges; for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { - if (isa<PathDiagnosticNotePiece>(I->get())) { + const auto &Piece = *I->get(); + + if (isa<PathDiagnosticPopUpPiece>(Piece)) { + ++IndexMap[NumRegularPieces]; + } else if (isa<PathDiagnosticNotePiece>(Piece)) { // This adds diagnostic bubbles, but not navigation. // Navigation through note pieces would be added later, // as a separate pass through the piece list. - HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); + HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces); --NumNotePieces; } else { - HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); + HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces, + TotalRegularPieces); --NumRegularPieces; } } - // Add line numbers, header, footer, etc. + // Secondary indexing if we are having multiple pop-ups between two notes. + // (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...) + NumRegularPieces = TotalRegularPieces; + for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { + const auto &Piece = *I->get(); + + if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) { + int PopUpPieceIndex = IndexMap[NumRegularPieces]; + + // Pop-up pieces needs the index of the last reported piece and its count + // how many times we report to handle multiple reports on the same range. + // This marks the variable, adds the </table> end tag and the message + // (list element) as a row. The <table> start tag will be added after the + // rows has been written out. Note: It stores every different range. + HandlePopUpPieceEndTag(R, *PopUpP, PopUpRanges, NumRegularPieces, + PopUpPieceIndex); + + if (PopUpPieceIndex > 0) + --IndexMap[NumRegularPieces]; + + } else if (!isa<PathDiagnosticNotePiece>(Piece)) { + --NumRegularPieces; + } + } + + // Add the <table> start tag of pop-up pieces based on the stored ranges. + HandlePopUpPieceStartTag(R, PopUpRanges); + // Add line numbers, header, footer, etc. html::EscapeText(R, FID); html::AddLineNumbers(R, FID); // If we have a preprocessor, relex the file and syntax highlight. // We might not have a preprocessor if we come from a deserialized AST file, // for example. - html::SyntaxHighlight(R, FID, PP); html::HighlightMacros(R, FID, PP); } -void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, - const PathDiagnosticPiece& P, +void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID, + const PathDiagnosticPiece &P, + const std::vector<SourceRange> &PopUpRanges, unsigned num, unsigned max) { // For now, just draw a box above the line in question, and emit the // warning. @@ -690,9 +778,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, bool IsNote = false; bool SuppressIndex = (max == 1); switch (P.getKind()) { - case PathDiagnosticPiece::Call: - llvm_unreachable("Calls and extra notes should already be handled"); - case PathDiagnosticPiece::Event: Kind = "Event"; break; + case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. case PathDiagnosticPiece::Macro: Kind = "Control"; break; @@ -701,6 +787,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, IsNote = true; SuppressIndex = true; break; + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::PopUp: + llvm_unreachable("Calls and extra notes should already be handled"); } std::string sbuf; @@ -860,8 +949,14 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Now highlight the ranges. ArrayRef<SourceRange> Ranges = P.getRanges(); - for (const auto &Range : Ranges) + for (const auto &Range : Ranges) { + // If we have already highlighted the range as a pop-up there is no work. + if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) != + PopUpRanges.end()) + continue; + HighlightRange(R, LPosInfo.first, Range); + } } static void EmitAlphaCounter(raw_ostream &os, unsigned n) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp index 6c55c61dd399..e7497f3fbdaa 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -1,9 +1,8 @@ //===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/IssueHash.h" @@ -121,7 +120,7 @@ static std::string GetEnclosingDeclContextSignature(const Decl *D) { return ""; } -static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) { +static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) { if (!Buffer) return ""; @@ -145,7 +144,7 @@ static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, col++; SourceLocation StartOfLine = SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); - llvm::MemoryBuffer *Buffer = + const llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine); if (!Buffer) return {}; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index da4574c61515..9838249ae82c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -1,9 +1,8 @@ //===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -235,7 +234,7 @@ bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) { ProgramPoint P = N->getLocation(); if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) - S = BE->getBlock()->getTerminator(); + S = BE->getBlock()->getTerminatorStmt(); if (S == LoopStmt) return false; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp index 8f6cb9a6b09e..9a7b1a24b819 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -1,9 +1,8 @@ //===--- LoopWidening.cpp - Widen loops -------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index 9a1d4d73c20b..f763701af7fb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1,9 +1,8 @@ //===- MemRegion.cpp - Abstract memory regions for static analysis --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -845,6 +844,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC, const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { + D = D->getCanonicalDecl(); const MemRegion *sReg = nullptr; if (D->hasGlobalStorage() && !D->isStaticLocal()) { @@ -931,6 +931,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, const MemRegion *superR) { + D = D->getCanonicalDecl(); return getSubRegion<VarRegion>(D, superR); } @@ -1008,6 +1009,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, const FunctionCodeRegion * MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { + // To think: should we canonicalize the declaration here? return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion()); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3e93bb6a7c4f..54fbd6a5bc49 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -1,9 +1,8 @@ //===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -91,6 +90,8 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; +PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; + void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (auto &Piece : *this) { @@ -120,6 +121,7 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: Current.push_back(Piece); break; } @@ -370,15 +372,16 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X, case PathDiagnosticPiece::ControlFlow: return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), cast<PathDiagnosticControlFlowPiece>(Y)); - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::Note: - return None; case PathDiagnosticPiece::Macro: return compareMacro(cast<PathDiagnosticMacroPiece>(X), cast<PathDiagnosticMacroPiece>(Y)); case PathDiagnosticPiece::Call: return compareCall(cast<PathDiagnosticCallPiece>(X), cast<PathDiagnosticCallPiece>(Y)); + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: + return None; } llvm_unreachable("all cases handled"); } @@ -572,6 +575,8 @@ static SourceLocation getValidSourceLocation(const Stmt* S, } while (!L.isValid()); } + // FIXME: Ironically, this assert actually fails in some cases. + //assert(L.isValid()); return L; } @@ -672,7 +677,15 @@ PathDiagnosticLocation::createConditionalColonLoc( PathDiagnosticLocation PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, const SourceManager &SM) { - return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); + + assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid()); + + // In some cases, getMemberLoc isn't valid -- in this case we'll return with + // some other related valid SourceLocation. + if (ME->getMemberLoc().isValid()) + return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); + + return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK); } PathDiagnosticLocation @@ -715,7 +728,24 @@ PathDiagnosticLocation::create(const ProgramPoint& P, const Stmt* S = nullptr; if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { const CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); + if (BSrc->getTerminator().isVirtualBaseBranch()) { + // TODO: VirtualBaseBranches should also appear for destructors. + // In this case we should put the diagnostic at the end of decl. + return PathDiagnosticLocation::createBegin( + P.getLocationContext()->getDecl(), SMng); + + } else { + S = BSrc->getTerminatorCondition(); + if (!S) { + // If the BlockEdge has no terminator condition statement but its + // source is the entry of the CFG (e.g. a checker crated the branch at + // the beginning of a function), use the function's declaration instead. + assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no " + "TerminatorCondition and is not the enrty block of the CFG"); + return PathDiagnosticLocation::createBegin( + P.getLocationContext()->getDecl(), SMng); + } + } } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { S = SP->getStmt(); if (P.getAs<PostStmtPurgeDeadSymbols>()) @@ -735,6 +765,12 @@ PathDiagnosticLocation::create(const ProgramPoint& P, return getLocationForCaller(CEE->getCalleeContext(), CEE->getLocationContext(), SMng); + } else if (auto CEB = P.getAs<CallExitBegin>()) { + if (const ReturnStmt *RS = CEB->getReturnStmt()) + return PathDiagnosticLocation::createBegin(RS, SMng, + CEB->getLocationContext()); + return PathDiagnosticLocation( + CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { CFGElement BlockFront = BE->getBlock()->front(); if (auto StmtElt = BlockFront.getAs<CFGStmt>()) { @@ -744,6 +780,9 @@ PathDiagnosticLocation::create(const ProgramPoint& P, NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); } llvm_unreachable("Unexpected CFG element at front of block"); + } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) { + return PathDiagnosticLocation(FE->getStmt(), SMng, + FE->getLocationContext()); } else { llvm_unreachable("Unexpected ProgramPoint"); } @@ -779,7 +818,7 @@ const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { if (auto SP = P.getAs<StmtPoint>()) return SP->getStmt(); if (auto BE = P.getAs<BlockEdge>()) - return BE->getSrc()->getTerminator(); + return BE->getSrc()->getTerminatorStmt(); if (auto CE = P.getAs<CallEnter>()) return CE->getCallExpr(); if (auto CEE = P.getAs<CallExitEnd>()) @@ -1255,6 +1294,10 @@ void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticSpotPiece::Profile(ID); } +void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); +} + void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { ID.Add(getLocation()); ID.AddString(BugType); @@ -1380,6 +1423,13 @@ LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { getLocation().dump(); } +LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const { + llvm::errs() << "POP-UP\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { if (!isValid()) { llvm::errs() << "<INVALID>\n"; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index db4cf76578d8..838751279297 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -1,9 +1,8 @@ //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" @@ -120,6 +120,9 @@ private: case PathDiagnosticPiece::Note: ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent); break; + case PathDiagnosticPiece::PopUp: + ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent); + break; } } @@ -138,6 +141,9 @@ private: unsigned indent, unsigned depth); void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, unsigned indent); + + void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P, + unsigned indent); }; } // end of anonymous namespace @@ -397,6 +403,34 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, Indent(o, indent); o << "</dict>\n"; } +void PlistPrinter::ReportPopUp(raw_ostream &o, + const PathDiagnosticPopUpPiece &P, + unsigned indent) { + const SourceManager &SM = PP.getSourceManager(); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + Indent(o, indent) << "<key>kind</key><string>pop-up</string>\n"; + + // Output the location. + FullSourceLoc L = P.getLocation().asLocation(); + + Indent(o, indent) << "<key>location</key>\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef<SourceRange> Ranges = P.getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the text. + EmitMessage(o, P.getString(), indent); + + // Finish up. + --indent; + Indent(o, indent) << "</dict>\n"; +} + //===----------------------------------------------------------------------===// // Static function definitions. //===----------------------------------------------------------------------===// @@ -714,7 +748,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( } // Finish. - o << "</dict>\n</plist>"; + o << "</dict>\n</plist>\n"; } //===----------------------------------------------------------------------===// @@ -777,10 +811,20 @@ public: /// As we expand the last line, we'll immediately replace PRINT(str) with /// print(x). The information that both 'str' and 'x' refers to the same string /// is an information we have to forward, hence the argument \p PrevArgs. -static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs); +/// +/// To avoid infinite recursion we maintain the already processed tokens in +/// a set. This is carried as a parameter through the recursive calls. The set +/// is extended with the currently processed token and after processing it, the +/// token is removed. If the token is already in the set, then recursion stops: +/// +/// #define f(y) x +/// #define x f(x) +static std::string getMacroNameAndPrintExpansion( + TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs, + llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens); /// Retrieves the name of the macro and what it's arguments expand into /// at \p ExpanLoc. @@ -829,19 +873,38 @@ static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, llvm::SmallString<200> ExpansionBuf; llvm::raw_svector_ostream OS(ExpansionBuf); TokenPrinter Printer(OS, PP); + llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; + std::string MacroName = - getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}); + getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, + AlreadyProcessedTokens); return { MacroName, OS.str() }; } -static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs) { +static std::string getMacroNameAndPrintExpansion( + TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs, + llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) { const SourceManager &SM = PP.getSourceManager(); MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); + IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name); + + // TODO: If the macro definition contains another symbol then this function is + // called recursively. In case this symbol is the one being defined, it will + // be an infinite recursion which is stopped by this "if" statement. However, + // in this case we don't get the full expansion text in the Plist file. See + // the test file where "value" is expanded to "garbage_" instead of + // "garbage_value". + if (AlreadyProcessedTokens.find(IDInfo) != AlreadyProcessedTokens.end()) + return Info.Name; + AlreadyProcessedTokens.insert(IDInfo); + + if (!Info.MI) + return Info.Name; // Manually expand its arguments from the previous macro. Info.Args.expandFromPrevMacro(PrevArgs); @@ -863,14 +926,15 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, // If this token is a macro that should be expanded inside the current // macro. - if (const MacroInfo *MI = - getMacroInfoForLocation(PP, SM, II, T.getLocation())) { - getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args); + if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) { + getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args, + AlreadyProcessedTokens); // If this is a function-like macro, skip its arguments, as // getExpandedMacro() already printed them. If this is the case, let's // first jump to the '(' token. - if (MI->getNumParams() != 0) + auto N = std::next(It); + if (N != E && N->is(tok::l_paren)) It = getMatchingRParen(++It, E); continue; } @@ -897,8 +961,17 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, } getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, - Info.Args); - if (MI->getNumParams() != 0) + Info.Args, AlreadyProcessedTokens); + // Peek the next token if it is a tok::l_paren. This way we can decide + // if this is the application or just a reference to a function maxro + // symbol: + // + // #define apply(f) ... + // #define func(x) ... + // apply(func) + // apply(func(42)) + auto N = std::next(ArgIt); + if (N != ArgEnd && N->is(tok::l_paren)) ArgIt = getMatchingRParen(++ArgIt, ArgEnd); } continue; @@ -909,6 +982,8 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, Printer.printToken(T); } + AlreadyProcessedTokens.erase(IDInfo); + return Info.Name; } @@ -937,7 +1012,14 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, assert(II && "Failed to acquire the IndetifierInfo for the macro!"); const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc); - assert(MI && "The macro must've been defined at it's expansion location!"); + // assert(MI && "The macro must've been defined at it's expansion location!"); + // + // We should always be able to obtain the MacroInfo in a given TU, but if + // we're running the analyzer with CTU, the Preprocessor won't contain the + // directive history (or anything for that matter) from another TU. + // TODO: assert when we're not running with CTU. + if (!MI) + return { MacroName, MI, {} }; // Acquire the macro's arguments. // @@ -951,8 +1033,16 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, return { MacroName, MI, {} }; RawLexer.LexFromRawLexer(TheTok); - assert(TheTok.is(tok::l_paren) && - "The token after the macro's identifier token should be '('!"); + // When this is a token which expands to another macro function then its + // parentheses are not at its expansion locaiton. For example: + // + // #define foo(x) int bar() { return x; } + // #define apply_zero(f) f(0) + // apply_zero(foo) + // ^ + // This is not a tok::l_paren, but foo is a function. + if (TheTok.isNot(tok::l_paren)) + return { MacroName, MI, {} }; MacroArgMap Args; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h index 4bb694819c2a..c71ee3bd4286 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h @@ -1,9 +1,8 @@ //==- PrettyStackTraceLocationContext.h - show analysis backtrace --*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -33,9 +32,9 @@ public: assert(LCtx); } - void print(raw_ostream &OS) const override { - OS << "While analyzing stack: \n"; - LCtx->dumpStack(OS, "\t"); + void print(raw_ostream &Out) const override { + Out << "While analyzing stack: \n"; + LCtx->dumpStack(Out); } }; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp index 2e2e2ec94f39..a1ca0b1b84bf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -1,9 +1,8 @@ //= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--= // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -11,14 +10,14 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/Analysis/CFG.h" +#include "clang/Basic/JsonSupport.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -442,53 +441,40 @@ void ProgramState::setStore(const StoreRef &newStore) { // State pretty-printing. //===----------------------------------------------------------------------===// -void ProgramState::print(raw_ostream &Out, - const char *NL, const char *Sep, - const LocationContext *LC) const { - // Print the store. +void ProgramState::printJson(raw_ostream &Out, const LocationContext *LCtx, + const char *NL, unsigned int Space, + bool IsDot) const { + Indent(Out, Space, IsDot) << "\"program_state\": {" << NL; + ++Space; + ProgramStateManager &Mgr = getStateManager(); - const ASTContext &Context = getStateManager().getContext(); - Mgr.getStoreManager().print(getStore(), Out, NL); + + // Print the store. + Mgr.getStoreManager().printJson(Out, getStore(), NL, Space, IsDot); // Print out the environment. - Env.print(Out, NL, Sep, Context, LC); + Env.printJson(Out, Mgr.getContext(), LCtx, NL, Space, IsDot); // Print out the constraints. - Mgr.getConstraintManager().print(this, Out, NL, Sep); + Mgr.getConstraintManager().printJson(Out, this, NL, Space, IsDot); // Print out the tracked dynamic types. - printDynamicTypeInfo(this, Out, NL, Sep); - - // Print out tainted symbols. - printTaint(Out, NL); + printDynamicTypeInfoJson(Out, this, NL, Space, IsDot); // Print checker-specific data. - Mgr.getOwningEngine().printState(Out, this, NL, Sep, LC); -} - -void ProgramState::printDOT(raw_ostream &Out, - const LocationContext *LC) const { - print(Out, "\\l", "\\|", LC); -} + Mgr.getOwningEngine().printJson(Out, this, LCtx, NL, Space, IsDot); -LLVM_DUMP_METHOD void ProgramState::dump() const { - print(llvm::errs()); + --Space; + Indent(Out, Space, IsDot) << '}'; } -void ProgramState::printTaint(raw_ostream &Out, - const char *NL) const { - TaintMapImpl TM = get<TaintMap>(); - - if (!TM.isEmpty()) - Out <<"Tainted symbols:" << NL; - - for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { - Out << I->first << " : " << I->second << NL; - } +void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LCtx, + unsigned int Space) const { + printJson(Out, LCtx, /*NL=*/"\\l", Space, /*IsDot=*/true); } -void ProgramState::dumpTaint() const { - printTaint(llvm::errs()); +LLVM_DUMP_METHOD void ProgramState::dump() const { + printJson(llvm::errs()); } AnalysisManager& ProgramState::getAnalysisManager() const { @@ -658,166 +644,3 @@ bool ProgramState::scanReachableSymbols( } return true; } - -ProgramStateRef ProgramState::addTaint(const Stmt *S, - const LocationContext *LCtx, - TaintTagType Kind) const { - if (const Expr *E = dyn_cast_or_null<Expr>(S)) - S = E->IgnoreParens(); - - return addTaint(getSVal(S, LCtx), Kind); -} - -ProgramStateRef ProgramState::addTaint(SVal V, - TaintTagType Kind) const { - SymbolRef Sym = V.getAsSymbol(); - if (Sym) - return addTaint(Sym, Kind); - - // If the SVal represents a structure, try to mass-taint all values within the - // structure. For now it only works efficiently on lazy compound values that - // were conjured during a conservative evaluation of a function - either as - // return values of functions that return structures or arrays by value, or as - // values of structures or arrays passed into the function by reference, - // directly or through pointer aliasing. Such lazy compound values are - // characterized by having exactly one binding in their captured store within - // their parent region, which is a conjured symbol default-bound to the base - // region of the parent region. - if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<SVal> binding = getStateManager().StoreMgr->getDefaultBinding(*LCV)) { - if (SymbolRef Sym = binding->getAsSymbol()) - return addPartialTaint(Sym, LCV->getRegion(), Kind); - } - } - - const MemRegion *R = V.getAsRegion(); - return addTaint(R, Kind); -} - -ProgramStateRef ProgramState::addTaint(const MemRegion *R, - TaintTagType Kind) const { - if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) - return addTaint(SR->getSymbol(), Kind); - return this; -} - -ProgramStateRef ProgramState::addTaint(SymbolRef Sym, - TaintTagType Kind) const { - // If this is a symbol cast, remove the cast before adding the taint. Taint - // is cast agnostic. - while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) - Sym = SC->getOperand(); - - ProgramStateRef NewState = set<TaintMap>(Sym, Kind); - assert(NewState); - return NewState; -} - -ProgramStateRef ProgramState::addPartialTaint(SymbolRef ParentSym, - const SubRegion *SubRegion, - TaintTagType Kind) const { - // Ignore partial taint if the entire parent symbol is already tainted. - if (contains<TaintMap>(ParentSym) && *get<TaintMap>(ParentSym) == Kind) - return this; - - // Partial taint applies if only a portion of the symbol is tainted. - if (SubRegion == SubRegion->getBaseRegion()) - return addTaint(ParentSym, Kind); - - const TaintedSubRegions *SavedRegs = get<DerivedSymTaint>(ParentSym); - TaintedSubRegions Regs = - SavedRegs ? *SavedRegs : stateMgr->TSRFactory.getEmptyMap(); - - Regs = stateMgr->TSRFactory.add(Regs, SubRegion, Kind); - ProgramStateRef NewState = set<DerivedSymTaint>(ParentSym, Regs); - assert(NewState); - return NewState; -} - -bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx, - TaintTagType Kind) const { - if (const Expr *E = dyn_cast_or_null<Expr>(S)) - S = E->IgnoreParens(); - - SVal val = getSVal(S, LCtx); - return isTainted(val, Kind); -} - -bool ProgramState::isTainted(SVal V, TaintTagType Kind) const { - if (const SymExpr *Sym = V.getAsSymExpr()) - return isTainted(Sym, Kind); - if (const MemRegion *Reg = V.getAsRegion()) - return isTainted(Reg, Kind); - return false; -} - -bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const { - if (!Reg) - return false; - - // Element region (array element) is tainted if either the base or the offset - // are tainted. - if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) - return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K); - - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) - return isTainted(SR->getSymbol(), K); - - if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) - return isTainted(ER->getSuperRegion(), K); - - return false; -} - -bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { - if (!Sym) - return false; - - // Traverse all the symbols this symbol depends on to see if any are tainted. - for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); - SI != SE; ++SI) { - if (!isa<SymbolData>(*SI)) - continue; - - if (const TaintTagType *Tag = get<TaintMap>(*SI)) { - if (*Tag == Kind) - return true; - } - - if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) { - // If this is a SymbolDerived with a tainted parent, it's also tainted. - if (isTainted(SD->getParentSymbol(), Kind)) - return true; - - // If this is a SymbolDerived with the same parent symbol as another - // tainted SymbolDerived and a region that's a sub-region of that tainted - // symbol, it's also tainted. - if (const TaintedSubRegions *Regs = - get<DerivedSymTaint>(SD->getParentSymbol())) { - const TypedValueRegion *R = SD->getRegion(); - for (auto I : *Regs) { - // FIXME: The logic to identify tainted regions could be more - // complete. For example, this would not currently identify - // overlapping fields in a union as tainted. To identify this we can - // check for overlapping/nested byte offsets. - if (Kind == I.second && R->isSubRegionOf(I.first)) - return true; - } - } - } - - // If memory region is tainted, data is also tainted. - if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) { - if (isTainted(SRV->getRegion(), Kind)) - return true; - } - - // If this is a SymbolCast from a tainted value, it's also tainted. - if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) { - if (isTainted(SC->getOperand(), Kind)) - return true; - } - } - - return false; -} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index d9b58d0f5185..64724227395d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1,9 +1,8 @@ //== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -12,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -174,6 +174,22 @@ RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, return newRanges; } +// Returns a set containing the values in the receiving set, intersected with +// the range set passed as parameter. +RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, + const RangeSet &Other) const { + PrimRangeSet newRanges = F.getEmptySet(); + + for (iterator i = Other.begin(), e = Other.end(); i != e; ++i) { + RangeSet newPiece = Intersect(BV, F, i->From(), i->To()); + for (iterator j = newPiece.begin(), ee = newPiece.end(); j != ee; ++j) { + newRanges = F.add(newRanges, *j); + } + } + + return newRanges; +} + // Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set // [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal // signed values of the type. @@ -231,6 +247,11 @@ public: // Implementation for interface from ConstraintManager. //===------------------------------------------------------------------===// + bool haveEqualConstraints(ProgramStateRef S1, + ProgramStateRef S2) const override { + return S1->get<ConstraintRange>() == S2->get<ConstraintRange>(); + } + bool canReasonAbout(SVal X) const override; ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; @@ -241,8 +262,8 @@ public: ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; - void print(ProgramStateRef State, raw_ostream &Out, const char *nl, - const char *sep) override; + void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const override; //===------------------------------------------------------------------===// // Implementation for interface from RangedConstraintManager. @@ -457,14 +478,21 @@ static RangeSet applyBitwiseConstraints( RangeSet RangeConstraintManager::getRange(ProgramStateRef State, SymbolRef Sym) { - if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym)) - return *V; - - BasicValueFactory &BV = getBasicVals(); + ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym); // If Sym is a difference of symbols A - B, then maybe we have range set // stored for B - A. - if (const RangeSet *R = getRangeForMinusSymbol(State, Sym)) + BasicValueFactory &BV = getBasicVals(); + const RangeSet *R = getRangeForMinusSymbol(State, Sym); + + // If we have range set stored for both A - B and B - A then calculate the + // effective range set by intersecting the range set for A - B and the + // negated range set of B - A. + if (V && R) + return V->Intersect(BV, F, R->Negate(BV, F)); + if (V) + return *V; + if (R) return R->Negate(BV, F); // Lazily generate a new RangeSet representing all possible values for the @@ -727,25 +755,35 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); } -//===------------------------------------------------------------------------=== +//===----------------------------------------------------------------------===// // Pretty-printing. -//===------------------------------------------------------------------------===/ - -void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, - const char *nl, const char *sep) { +//===----------------------------------------------------------------------===// - ConstraintRangeTy Ranges = St->get<ConstraintRange>(); +void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, + bool IsDot) const { + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); - if (Ranges.isEmpty()) { - Out << nl << sep << "Ranges are empty." << nl; + Indent(Out, Space, IsDot) << "\"constraints\": "; + if (Constraints.isEmpty()) { + Out << "null," << NL; return; } - Out << nl << sep << "Ranges of symbol values:"; - for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E; - ++I) { - Out << nl << ' ' << I.getKey() << " : "; + ++Space; + Out << '[' << NL; + for (ConstraintRangeTy::iterator I = Constraints.begin(); + I != Constraints.end(); ++I) { + Indent(Out, Space, IsDot) + << "{ \"symbol\": \"" << I.getKey() << "\", \"range\": \""; I.getData().print(Out); + Out << "\" }"; + + if (std::next(I) != Constraints.end()) + Out << ','; + Out << NL; } - Out << nl; + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp index 146dc20ad021..4748c106eb55 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -1,9 +1,8 @@ //== RangedConstraintManager.cpp --------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index b2339be4f263..d2aea1fd92dd 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1,9 +1,8 @@ //== RegionStore.cpp - Field-sensitive store model --------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -20,6 +19,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -121,25 +121,21 @@ BindingKey BindingKey::Make(const MemRegion *R, Kind k) { } namespace llvm { - static inline - raw_ostream &operator<<(raw_ostream &os, BindingKey K) { - os << '(' << K.getRegion(); - if (!K.hasSymbolicOffset()) - os << ',' << K.getOffset(); - os << ',' << (K.isDirect() ? "direct" : "default") - << ')'; - return os; - } - - template <typename T> struct isPodLike; - template <> struct isPodLike<BindingKey> { - static const bool value = true; - }; -} // end llvm namespace - -#ifndef NDEBUG +static inline raw_ostream &operator<<(raw_ostream &Out, BindingKey K) { + Out << "\"kind\": \"" << (K.isDirect() ? "Direct" : "Default") + << "\", \"offset\": "; + + if (!K.hasSymbolicOffset()) + Out << K.getOffset(); + else + Out << "null"; + + return Out; +} + +} // namespace llvm + LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; } -#endif //===----------------------------------------------------------------------===// // Actual Store type. @@ -211,18 +207,34 @@ public: return asImmutableMap().getRootWithoutRetain(); } - void dump(raw_ostream &OS, const char *nl) const { - for (iterator I = begin(), E = end(); I != E; ++I) { - const ClusterBindings &Cluster = I.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; - } - OS << nl; - } + void printJson(raw_ostream &Out, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const { + for (iterator I = begin(); I != end(); ++I) { + // TODO: We might need a .printJson for I.getKey() as well. + Indent(Out, Space, IsDot) + << "{ \"cluster\": \"" << I.getKey() << "\", \"pointer\": \"" + << (const void *)I.getKey() << "\", \"items\": [" << NL; + + ++Space; + const ClusterBindings &CB = I.getData(); + for (ClusterBindings::iterator CI = CB.begin(); CI != CB.end(); ++CI) { + Indent(Out, Space, IsDot) << "{ " << CI.getKey() << ", \"value\": "; + CI.getData().printJson(Out, /*AddQuotes=*/true); + Out << " }"; + if (std::next(CI) != CB.end()) + Out << ','; + Out << NL; + } + + --Space; + Indent(Out, Space, IsDot) << "]}"; + if (std::next(I) != end()) + Out << ','; + Out << NL; + } } - LLVM_DUMP_METHOD void dump() const { dump(llvm::errs(), "\n"); } + LLVM_DUMP_METHOD void dump() const { printJson(llvm::errs()); } }; } // end anonymous namespace @@ -599,7 +611,8 @@ public: // Part of public interface to class. RBFactory.getTreeFactory()); } - void print(Store store, raw_ostream &Out, const char* nl) override; + void printJson(raw_ostream &Out, Store S, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const override; void iterBindings(Store store, BindingsHandler& f) override { RegionBindingsRef B = getRegionBindings(store); @@ -1240,7 +1253,7 @@ RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, // Bind the globals memory space to a new symbol that we will use to derive // the bindings for all globals. const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K); - SVal V = svalBuilder.conjureSymbolVal(/* SymbolTag = */ (const void*) GS, Ex, LCtx, + SVal V = svalBuilder.conjureSymbolVal(/* symbolTag = */ (const void*) GS, Ex, LCtx, /* type does not matter */ Ctx.IntTy, Count); @@ -1660,7 +1673,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const VarDecl *VD = VR->getDecl(); // Either the array or the array element has to be const. if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { // The array index has to be known. if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) { @@ -1750,7 +1763,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, unsigned Index = FD->getFieldIndex(); // Either the record variable or the field has to be const qualified. if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) - if (const Expr *Init = VD->getInit()) + if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { if (Index < InitList->getNumInits()) { if (const Expr *FieldInit = InitList->getInit(Index)) @@ -1932,7 +1945,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, const VarRegion *R) { // Check if the region has a binding. - if (const Optional<SVal> &V = B.getDirectBinding(R)) + if (Optional<SVal> V = B.getDirectBinding(R)) + return *V; + + if (Optional<SVal> V = B.getDefaultBinding(R)) return *V; // Lazily derive a value for the VarRegion. @@ -1945,7 +1961,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, // Is 'VD' declared constant? If so, retrieve the constant value. if (VD->getType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) return *V; @@ -2339,12 +2355,64 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>()) return bindAggregate(B, R, UnknownVal()); + // The raw CompoundVal is essentially a symbolic InitListExpr: an (immutable) + // list of other values. It appears pretty much only when there's an actual + // initializer list expression in the program, and the analyzer tries to + // unwrap it as soon as possible. + // This code is where such unwrap happens: when the compound value is put into + // the object that it was supposed to initialize (it's an *initializer* list, + // after all), instead of binding the whole value to the whole object, we bind + // sub-values to sub-objects. Sub-values may themselves be compound values, + // and in this case the procedure becomes recursive. + // FIXME: The annoying part about compound values is that they don't carry + // any sort of information about which value corresponds to which sub-object. + // It's simply a list of values in the middle of nowhere; we expect to match + // them to sub-objects, essentially, "by index": first value binds to + // the first field, second value binds to the second field, etc. + // It would have been much safer to organize non-lazy compound values as + // a mapping from fields/bases to values. const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - RecordDecl::field_iterator FI, FE; RegionBindingsRef NewB(B); + // In C++17 aggregates may have base classes, handle those as well. + // They appear before fields in the initializer list / compound value. + if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD)) { + // If the object was constructed with a constructor, its value is a + // LazyCompoundVal. If it's a raw CompoundVal, it means that we're + // performing aggregate initialization. The only exception from this + // rule is sending an Objective-C++ message that returns a C++ object + // to a nil receiver; in this case the semantics is to return a + // zero-initialized object even if it's a C++ object that doesn't have + // this sort of constructor; the CompoundVal is empty in this case. + assert((CRD->isAggregate() || (Ctx.getLangOpts().ObjC && VI == VE)) && + "Non-aggregates are constructed with a constructor!"); + + for (const auto &B : CRD->bases()) { + // (Multiple inheritance is fine though.) + assert(!B.isVirtual() && "Aggregates cannot have virtual base classes!"); + + if (VI == VE) + break; + + QualType BTy = B.getType(); + assert(BTy->isStructureOrClassType() && "Base classes must be classes!"); + + const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl(); + assert(BRD && "Base classes must be C++ classes!"); + + const CXXBaseObjectRegion *BR = + MRMgr.getCXXBaseObjectRegion(BRD, R, /*IsVirtual=*/false); + + NewB = bindStruct(NewB, BR, *VI); + + ++VI; + } + } + + RecordDecl::field_iterator FI, FE; + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) @@ -2391,10 +2459,7 @@ RegionStoreManager::bindAggregate(RegionBindingsConstRef B, namespace { class RemoveDeadBindingsWorker : public ClusterAnalysis<RemoveDeadBindingsWorker> { - using ChildrenListTy = SmallVector<const SymbolDerived *, 4>; - using MapParentsToDerivedTy = llvm::DenseMap<SymbolRef, ChildrenListTy>; - - MapParentsToDerivedTy ParentsToDerived; + SmallVector<const SymbolicRegion *, 12> Postponed; SymbolReaper &SymReaper; const StackFrameContext *CurrentLCtx; @@ -2415,10 +2480,8 @@ public: bool AddToWorkList(const MemRegion *R); + bool UpdatePostponed(); void VisitBinding(SVal V); - -private: - void populateWorklistFromSymbol(SymbolRef s); }; } @@ -2438,11 +2501,10 @@ void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) { - if (SymReaper.isLive(SR->getSymbol())) { + if (SymReaper.isLive(SR->getSymbol())) AddToWorkList(SR, &C); - } else if (const auto *SD = dyn_cast<SymbolDerived>(SR->getSymbol())) { - ParentsToDerived[SD->getParentSymbol()].push_back(SD); - } + else + Postponed.push_back(SR); return; } @@ -2455,7 +2517,7 @@ void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, // CXXThisRegion in the current or parent location context is live. if (const CXXThisRegion *TR = dyn_cast<CXXThisRegion>(baseR)) { const auto *StackReg = - cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); + cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); const StackFrameContext *RegCtx = StackReg->getStackFrame(); if (CurrentLCtx && (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx))) @@ -2499,15 +2561,6 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) { // If V is a region, then add it to the worklist. if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); - - if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) { - DefinedOrUnknownSVal RVS = - RM.getSValBuilder().getRegionValueSymbolVal(TVR); - if (const MemRegion *SR = RVS.getAsRegion()) { - AddToWorkList(SR); - } - } - SymReaper.markLive(R); // All regions captured by a block are also live. @@ -2521,30 +2574,25 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) { // Update the set of live symbols. - for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI != SE; ++SI) { - populateWorklistFromSymbol(*SI); - - for (const auto *SD : ParentsToDerived[*SI]) - populateWorklistFromSymbol(SD); - + for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI!=SE; ++SI) SymReaper.markLive(*SI); - } } -void RemoveDeadBindingsWorker::populateWorklistFromSymbol(SymbolRef S) { - if (const auto *SD = dyn_cast<SymbolData>(S)) { - if (Loc::isLocType(SD->getType()) && !SymReaper.isLive(SD)) { - const SymbolicRegion *SR = RM.getRegionManager().getSymbolicRegion(SD); - - if (B.contains(SR)) - AddToWorkList(SR); +bool RemoveDeadBindingsWorker::UpdatePostponed() { + // See if any postponed SymbolicRegions are actually live now, after + // having done a scan. + bool Changed = false; - const SymbolicRegion *SHR = - RM.getRegionManager().getSymbolicHeapRegion(SD); - if (B.contains(SHR)) - AddToWorkList(SHR); + for (auto I = Postponed.begin(), E = Postponed.end(); I != E; ++I) { + if (const SymbolicRegion *SR = *I) { + if (SymReaper.isLive(SR->getSymbol())) { + Changed |= AddToWorkList(SR); + *I = nullptr; + } } } + + return Changed; } StoreRef RegionStoreManager::removeDeadBindings(Store store, @@ -2560,7 +2608,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, W.AddToWorkList(*I); } - W.RunWorkList(); + do W.RunWorkList(); while (W.UpdatePostponed()); // We have now scanned the store, marking reachable regions and symbols // as live. We now remove all the regions that are dead from the store @@ -2581,11 +2629,18 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, // Utility methods. //===----------------------------------------------------------------------===// -void RegionStoreManager::print(Store store, raw_ostream &OS, - const char* nl) { - RegionBindingsRef B = getRegionBindings(store); - OS << "Store (direct and default bindings), " - << B.asStore() - << " :" << nl; - B.dump(OS, nl); +void RegionStoreManager::printJson(raw_ostream &Out, Store S, const char *NL, + unsigned int Space, bool IsDot) const { + RegionBindingsRef Bindings = getRegionBindings(S); + + Indent(Out, Space, IsDot) << "\"store\": "; + + if (Bindings.isEmpty()) { + Out << "null," << NL; + return; + } + + Out << "{ \"pointer\": \"" << Bindings.asStore() << "\", \"items\": [" << NL; + Bindings.printJson(Out, NL, Space + 1, IsDot); + Indent(Out, Space, IsDot) << "]}," << NL; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp deleted file mode 100644 index 2e40cc33381c..000000000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ /dev/null @@ -1,1229 +0,0 @@ -//== RetainSummaryManager.cpp - Summaries for reference counting --*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines summaries implementation for retain counting, which -// implements a reference count checker for Core Foundation, Cocoa -// and OSObject (on Mac OS X). -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "clang/AST/Attr.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/ParentMap.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang; -using namespace ento; - -template <class T> -constexpr static bool isOneOf() { - return false; -} - -/// Helper function to check whether the class is one of the -/// rest of varargs. -template <class T, class P, class... ToCompare> -constexpr static bool isOneOf() { - return std::is_same<T, P>::value || isOneOf<T, ToCompare...>(); -} - -namespace { - -/// Fake attribute class for RC* attributes. -struct GeneralizedReturnsRetainedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_returns_retained"; - return false; - } -}; - -struct GeneralizedReturnsNotRetainedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_returns_not_retained"; - return false; - } -}; - -struct GeneralizedConsumedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_consumed"; - return false; - } -}; - -} - -template <class T> -Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D, - QualType QT) { - ObjKind K; - if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr, - CFReturnsNotRetainedAttr>()) { - if (!TrackObjCAndCFObjects) - return None; - - K = ObjKind::CF; - } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr, - NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr, - NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) { - - if (!TrackObjCAndCFObjects) - return None; - - if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr, - NSReturnsNotRetainedAttr>() && - !cocoa::isCocoaObjectRef(QT)) - return None; - K = ObjKind::ObjC; - } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr, - OSReturnsNotRetainedAttr, OSReturnsRetainedAttr, - OSReturnsRetainedOnZeroAttr, - OSReturnsRetainedOnNonZeroAttr>()) { - if (!TrackOSObjects) - return None; - K = ObjKind::OS; - } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr, - GeneralizedReturnsRetainedAttr, - GeneralizedConsumedAttr>()) { - K = ObjKind::Generalized; - } else { - llvm_unreachable("Unexpected attribute"); - } - if (D->hasAttr<T>()) - return K; - return None; -} - -template <class T1, class T2, class... Others> -Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D, - QualType QT) { - if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT)) - return Out; - return hasAnyEnabledAttrOf<T2, Others...>(D, QT); -} - -const RetainSummary * -RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) { - // Unique "simple" summaries -- those without ArgEffects. - if (OldSumm.isSimple()) { - ::llvm::FoldingSetNodeID ID; - OldSumm.Profile(ID); - - void *Pos; - CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos); - - if (!N) { - N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>(); - new (N) CachedSummaryNode(OldSumm); - SimpleSummaries.InsertNode(N, Pos); - } - - return &N->getValue(); - } - - RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(OldSumm); - return Summ; -} - -static bool isSubclass(const Decl *D, - StringRef ClassName) { - using namespace ast_matchers; - DeclarationMatcher SubclassM = cxxRecordDecl(isSameOrDerivedFrom(ClassName)); - return !(match(SubclassM, *D, D->getASTContext()).empty()); -} - -static bool isOSObjectSubclass(const Decl *D) { - return isSubclass(D, "OSObject"); -} - -static bool isOSObjectDynamicCast(StringRef S) { - return S == "safeMetaCast"; -} - -static bool isOSIteratorSubclass(const Decl *D) { - return isSubclass(D, "OSIterator"); -} - -static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) { - for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) { - if (Ann->getAnnotation() == rcAnnotation) - return true; - } - return false; -} - -static bool isRetain(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("retain") || FName.endswith_lower("retain"); -} - -static bool isRelease(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("release") || FName.endswith_lower("release"); -} - -static bool isAutorelease(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("autorelease") || - FName.endswith_lower("autorelease"); -} - -static bool isMakeCollectable(StringRef FName) { - return FName.contains_lower("MakeCollectable"); -} - -/// A function is OSObject related if it is declared on a subclass -/// of OSObject, or any of the parameters is a subclass of an OSObject. -static bool isOSObjectRelated(const CXXMethodDecl *MD) { - if (isOSObjectSubclass(MD->getParent())) - return true; - - for (ParmVarDecl *Param : MD->parameters()) { - QualType PT = Param->getType()->getPointeeType(); - if (!PT.isNull()) - if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl()) - if (isOSObjectSubclass(RD)) - return true; - } - - return false; -} - -const RetainSummary * -RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, - StringRef FName, QualType RetTy) { - if (RetTy->isPointerType()) { - const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); - if (PD && isOSObjectSubclass(PD)) { - if (const IdentifierInfo *II = FD->getIdentifier()) { - if (isOSObjectDynamicCast(II->getName())) - return getDefaultSummary(); - - // All objects returned with functions *not* starting with - // get, or iterators, are returned at +1. - if ((!II->getName().startswith("get") && - !II->getName().startswith("Get")) || - isOSIteratorSubclass(PD)) { - return getOSSummaryCreateRule(FD); - } else { - return getOSSummaryGetRule(FD); - } - } - } - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) { - if (FName == "release") - return getOSSummaryReleaseRule(FD); - - if (FName == "retain") - return getOSSummaryRetainRule(FD); - - if (FName == "free") - return getOSSummaryFreeRule(FD); - - if (MD->getOverloadedOperator() == OO_New) - return getOSSummaryCreateRule(MD); - } - } - - return nullptr; -} - -const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject( - const FunctionDecl *FD, - StringRef FName, - QualType RetTy, - const FunctionType *FT, - bool &AllowAnnotations) { - - ArgEffects ScratchArgs(AF.getEmptyMap()); - - std::string RetTyName = RetTy.getAsString(); - if (FName == "pthread_create" || FName == "pthread_setspecific") { - // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. - // This will be addressed better with IPA. - return getPersistentStopSummary(); - } else if(FName == "NSMakeCollectable") { - // Handle: id NSMakeCollectable(CFTypeRef) - AllowAnnotations = false; - return RetTy->isObjCIdType() ? getUnarySummary(FT, DoNothing) - : getPersistentStopSummary(); - } else if (FName == "CMBufferQueueDequeueAndRetain" || - FName == "CMBufferQueueDequeueIfDataReadyAndRetain") { - // Part of: <rdar://problem/39390714>. - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), - ScratchArgs, - ArgEffect(DoNothing), - ArgEffect(DoNothing)); - } else if (FName == "CFPlugInInstanceCreate") { - return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs); - } else if (FName == "IORegistryEntrySearchCFProperty" || - (RetTyName == "CFMutableDictionaryRef" && - (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" || - FName == "IOServiceNameMatching" || - FName == "IORegistryEntryIDMatching" || - FName == "IOOpenFirmwarePathMatching"))) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "IOServiceGetMatchingService" || - FName == "IOServiceGetMatchingServices") { - // FIXES: <rdar://problem/6326900> - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(DecRef, ObjKind::CF)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "IOServiceAddNotification" || - FName == "IOServiceAddMatchingNotification") { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(DecRef, ObjKind::CF)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CVPixelBufferCreateWithBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithBytes is released via - // a callback and doing full IPA to make sure this is done correctly. - // FIXME: This function has an out parameter that returns an - // allocated object. - ScratchArgs = AF.add(ScratchArgs, 7, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CGBitmapContextCreateWithData") { - // FIXES: <rdar://problem/7358899> - // Eventually this can be improved by recognizing that 'releaseInfo' - // passed to CGBitmapContextCreateWithData is released via - // a callback and doing full IPA to make sure this is done correctly. - ScratchArgs = AF.add(ScratchArgs, 8, ArgEffect(ArgEffect(StopTracking))); - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithPlanarBytes is released - // via a callback and doing full IPA to make sure this is done - // correctly. - ScratchArgs = AF.add(ScratchArgs, 12, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "VTCompressionSessionEncodeFrame") { - // The context argument passed to VTCompressionSessionEncodeFrame() - // is passed to the callback specified when creating the session - // (e.g. with VTCompressionSessionCreate()) which can release it. - // To account for this possibility, conservatively stop tracking - // the context. - ScratchArgs = AF.add(ScratchArgs, 5, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "dispatch_set_context" || - FName == "xpc_connection_set_context") { - // <rdar://problem/11059275> - The analyzer currently doesn't have - // a good way to reason about the finalizer function for libdispatch. - // If we pass a context object that is memory managed, stop tracking it. - // <rdar://problem/13783514> - Same problem, but for XPC. - // FIXME: this hack should possibly go away once we can handle - // libdispatch and XPC finalizers. - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName.startswith("NSLog")) { - return getDoNothingSummary(); - } else if (FName.startswith("NS") && - (FName.find("Insert") != StringRef::npos)) { - // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. (radar://11152419) - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking)); - ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, ArgEffect(DoNothing), - ArgEffect(DoNothing)); - } - - if (RetTy->isPointerType()) { - - // For CoreFoundation ('CF') types. - if (cocoa::isRefType(RetTy, "CF", FName)) { - if (isRetain(FD, FName)) { - // CFRetain isn't supposed to be annotated. However, this may as - // well be a user-made "safe" CFRetain function that is incorrectly - // annotated as cf_returns_retained due to lack of better options. - // We want to ignore such annotation. - AllowAnnotations = false; - - return getUnarySummary(FT, IncRef); - } else if (isAutorelease(FD, FName)) { - // The headers use cf_consumed, but we can fully model CFAutorelease - // ourselves. - AllowAnnotations = false; - - return getUnarySummary(FT, Autorelease); - } else if (isMakeCollectable(FName)) { - AllowAnnotations = false; - return getUnarySummary(FT, DoNothing); - } else { - return getCFCreateGetRuleSummary(FD); - } - } - - // For CoreGraphics ('CG') and CoreVideo ('CV') types. - if (cocoa::isRefType(RetTy, "CG", FName) || - cocoa::isRefType(RetTy, "CV", FName)) { - if (isRetain(FD, FName)) - return getUnarySummary(FT, IncRef); - else - return getCFCreateGetRuleSummary(FD); - } - - // For all other CF-style types, use the Create/Get - // rule for summaries but don't support Retain functions - // with framework-specific prefixes. - if (coreFoundation::isCFObjectRef(RetTy)) { - return getCFCreateGetRuleSummary(FD); - } - - if (FD->hasAttr<CFAuditedTransferAttr>()) { - return getCFCreateGetRuleSummary(FD); - } - } - - // Check for release functions, the only kind of functions that we care - // about that don't return a pointer type. - if (FName.startswith("CG") || FName.startswith("CF")) { - // Test for 'CGCF'. - FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); - - if (isRelease(FD, FName)) - return getUnarySummary(FT, DecRef); - else { - assert(ScratchArgs.isEmpty()); - // Remaining CoreFoundation and CoreGraphics functions. - // We use to assume that they all strictly followed the ownership idiom - // and that ownership cannot be transferred. While this is technically - // correct, many methods allow a tracked object to escape. For example: - // - // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); - // CFDictionaryAddValue(y, key, x); - // CFRelease(x); - // ... it is okay to use 'x' since 'y' has a reference to it - // - // We handle this and similar cases with the follow heuristic. If the - // function name contains "InsertValue", "SetValue", "AddValue", - // "AppendValue", or "SetAttribute", then we assume that arguments may - // "escape." This means that something else holds on to the object, - // allowing it be used even after its local retain count drops to 0. - ArgEffectKind E = - (StrInStrNoCase(FName, "InsertValue") != StringRef::npos || - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos || - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) - ? MayEscape - : DoNothing; - - return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(E, ObjKind::CF)); - } - } - - return nullptr; -} - -const RetainSummary * -RetainSummaryManager::generateSummary(const FunctionDecl *FD, - bool &AllowAnnotations) { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) - return getPersistentStopSummary(); - - const IdentifierInfo *II = FD->getIdentifier(); - - StringRef FName = II ? II->getName() : ""; - - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - FName = FName.substr(FName.find_first_not_of('_')); - - // Inspect the result type. Strip away any typedefs. - const auto *FT = FD->getType()->getAs<FunctionType>(); - QualType RetTy = FT->getReturnType(); - - if (TrackOSObjects) - if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy)) - return S; - - if (TrackObjCAndCFObjects) - if (const RetainSummary *S = - getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations)) - return S; - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) - if (!(TrackOSObjects && isOSObjectRelated(MD))) - return getPersistentSummary(RetEffect::MakeNoRet(), - ArgEffects(AF.getEmptyMap()), - ArgEffect(DoNothing), - ArgEffect(StopTracking), - ArgEffect(DoNothing)); - - return getDefaultSummary(); -} - -const RetainSummary * -RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { - // If we don't know what function we're calling, use our default summary. - if (!FD) - return getDefaultSummary(); - - // Look up a summary in our cache of FunctionDecls -> Summaries. - FuncSummariesTy::iterator I = FuncSummaries.find(FD); - if (I != FuncSummaries.end()) - return I->second; - - // No summary? Generate one. - bool AllowAnnotations = true; - const RetainSummary *S = generateSummary(FD, AllowAnnotations); - - // Annotations override defaults. - if (AllowAnnotations) - updateSummaryFromAnnotations(S, FD); - - FuncSummaries[FD] = S; - return S; -} - -//===----------------------------------------------------------------------===// -// Summary creation for functions (largely uses of Core Foundation). -//===----------------------------------------------------------------------===// - -static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) { - switch (E.getKind()) { - case DoNothing: - case Autorelease: - case DecRefBridgedTransferred: - case IncRef: - case UnretainedOutParameter: - case RetainedOutParameter: - case RetainedOutParameterOnZero: - case RetainedOutParameterOnNonZero: - case MayEscape: - case StopTracking: - case StopTrackingHard: - return E.withKind(StopTrackingHard); - case DecRef: - case DecRefAndStopTrackingHard: - return E.withKind(DecRefAndStopTrackingHard); - case Dealloc: - return E.withKind(Dealloc); - } - - llvm_unreachable("Unknown ArgEffect kind"); -} - -void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, - const CallEvent &Call) { - if (Call.hasNonZeroCallbackArg()) { - ArgEffect RecEffect = - getStopTrackingHardEquivalent(S->getReceiverEffect()); - ArgEffect DefEffect = - getStopTrackingHardEquivalent(S->getDefaultArgEffect()); - - ArgEffects ScratchArgs(AF.getEmptyMap()); - ArgEffects CustomArgEffects = S->getArgEffects(); - for (ArgEffects::iterator I = CustomArgEffects.begin(), - E = CustomArgEffects.end(); - I != E; ++I) { - ArgEffect Translated = getStopTrackingHardEquivalent(I->second); - if (Translated.getKind() != DefEffect.getKind()) - ScratchArgs = AF.add(ScratchArgs, I->first, Translated); - } - - RetEffect RE = RetEffect::MakeNoRetHard(); - - // Special cases where the callback argument CANNOT free the return value. - // This can generally only happen if we know that the callback will only be - // called when the return value is already being deallocated. - if (const SimpleFunctionCall *FC = dyn_cast<SimpleFunctionCall>(&Call)) { - if (IdentifierInfo *Name = FC->getDecl()->getIdentifier()) { - // When the CGBitmapContext is deallocated, the callback here will free - // the associated data buffer. - // The callback in dispatch_data_create frees the buffer, but not - // the data object. - if (Name->isStr("CGBitmapContextCreateWithData") || - Name->isStr("dispatch_data_create")) - RE = S->getRetEffect(); - } - } - - S = getPersistentSummary(RE, ScratchArgs, RecEffect, DefEffect); - } - - // Special case '[super init];' and '[self init];' - // - // Even though calling '[super init]' without assigning the result to self - // and checking if the parent returns 'nil' is a bad pattern, it is common. - // Additionally, our Self Init checker already warns about it. To avoid - // overwhelming the user with messages from both checkers, we model the case - // of '[super init]' in cases when it is not consumed by another expression - // as if the call preserves the value of 'self'; essentially, assuming it can - // never fail and return 'nil'. - // Note, we don't want to just stop tracking the value since we want the - // RetainCount checker to report leaks and use-after-free if SelfInit checker - // is turned off. - if (const ObjCMethodCall *MC = dyn_cast<ObjCMethodCall>(&Call)) { - if (MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper()) { - - // Check if the message is not consumed, we know it will not be used in - // an assignment, ex: "self = [super init]". - const Expr *ME = MC->getOriginExpr(); - const LocationContext *LCtx = MC->getLocationContext(); - ParentMap &PM = LCtx->getAnalysisDeclContext()->getParentMap(); - if (!PM.isConsumedExpr(ME)) { - RetainSummaryTemplate ModifiableSummaryTemplate(S, *this); - ModifiableSummaryTemplate->setReceiverEffect(ArgEffect(DoNothing)); - ModifiableSummaryTemplate->setRetEffect(RetEffect::MakeNoRet()); - } - } - } -} - -const RetainSummary * -RetainSummaryManager::getSummary(const CallEvent &Call, - QualType ReceiverType) { - const RetainSummary *Summ; - switch (Call.getKind()) { - case CE_Function: - case CE_CXXMember: - case CE_CXXMemberOperator: - case CE_CXXConstructor: - case CE_CXXAllocator: - Summ = getFunctionSummary(cast_or_null<FunctionDecl>(Call.getDecl())); - break; - case CE_Block: - case CE_CXXDestructor: - // FIXME: These calls are currently unsupported. - return getPersistentStopSummary(); - case CE_ObjCMessage: { - const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); - if (Msg.isInstanceMessage()) - Summ = getInstanceMethodSummary(Msg, ReceiverType); - else - Summ = getClassMethodSummary(Msg); - break; - } - } - - updateSummaryForCall(Summ, Call); - - assert(Summ && "Unknown call type?"); - return Summ; -} - - -const RetainSummary * -RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) { - if (coreFoundation::followsCreateRule(FD)) - return getCFSummaryCreateRule(FD); - - return getCFSummaryGetRule(FD); -} - -bool RetainSummaryManager::isTrustedReferenceCountImplementation( - const FunctionDecl *FD) { - return hasRCAnnotation(FD, "rc_ownership_trusted_implementation"); -} - -Optional<RetainSummaryManager::BehaviorSummary> -RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD, - bool &hasTrustedImplementationAnnotation) { - - IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return None; - - StringRef FName = II->getName(); - FName = FName.substr(FName.find_first_not_of('_')); - - QualType ResultTy = CE->getCallReturnType(Ctx); - if (ResultTy->isObjCIdType()) { - if (II->isStr("NSMakeCollectable")) - return BehaviorSummary::Identity; - } else if (ResultTy->isPointerType()) { - // Handle: (CF|CG|CV)Retain - // CFAutorelease - // It's okay to be a little sloppy here. - if (FName == "CMBufferQueueDequeueAndRetain" || - FName == "CMBufferQueueDequeueIfDataReadyAndRetain") { - // Part of: <rdar://problem/39390714>. - // These are not retain. They just return something and retain it. - return None; - } - if (cocoa::isRefType(ResultTy, "CF", FName) || - cocoa::isRefType(ResultTy, "CG", FName) || - cocoa::isRefType(ResultTy, "CV", FName)) - if (isRetain(FD, FName) || isAutorelease(FD, FName) || - isMakeCollectable(FName)) - return BehaviorSummary::Identity; - - // safeMetaCast is called by OSDynamicCast. - // We assume that OSDynamicCast is either an identity (cast is OK, - // the input was non-zero), - // or that it returns zero (when the cast failed, or the input - // was zero). - if (TrackOSObjects && isOSObjectDynamicCast(FName)) { - return BehaviorSummary::IdentityOrZero; - } - - const FunctionDecl* FDD = FD->getDefinition(); - if (FDD && isTrustedReferenceCountImplementation(FDD)) { - hasTrustedImplementationAnnotation = true; - return BehaviorSummary::Identity; - } - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) - if (FName == "release" || FName == "retain") - return BehaviorSummary::NoOp; - } - - return None; -} - -const RetainSummary * -RetainSummaryManager::getUnarySummary(const FunctionType* FT, - ArgEffectKind AE) { - - // Unary functions have no arg effects by definition. - ArgEffects ScratchArgs(AF.getEmptyMap()); - - // Sanity check that this is *really* a unary function. This can - // happen if people do weird things. - const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT); - if (!FTP || FTP->getNumParams() != 1) - return getPersistentStopSummary(); - - ArgEffect Effect(AE, ObjKind::CF); - - ScratchArgs = AF.add(ScratchArgs, 0, Effect); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::OS), - AF.getEmptyMap()); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::OS), - AF.getEmptyMap()); -} - -const RetainSummary * -RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), - ArgEffects(AF.getEmptyMap())); -} - -const RetainSummary * -RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::CF), - ArgEffects(AF.getEmptyMap()), - ArgEffect(DoNothing), ArgEffect(DoNothing)); -} - - - - -//===----------------------------------------------------------------------===// -// Summary creation for Selectors. -//===----------------------------------------------------------------------===// - -Optional<RetEffect> -RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, - const Decl *D) { - if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, RetTy)) - return ObjCAllocRetE; - - if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr, - GeneralizedReturnsRetainedAttr>(D, RetTy)) - return RetEffect::MakeOwned(*K); - - if (auto K = hasAnyEnabledAttrOf< - CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr, - GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr, - NSReturnsAutoreleasedAttr>(D, RetTy)) - return RetEffect::MakeNotOwned(*K); - - if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) - for (const auto *PD : MD->overridden_methods()) - if (auto RE = getRetEffectFromAnnotations(RetTy, PD)) - return RE; - - return None; -} - -/// \return Whether the chain of typedefs starting from {@code QT} -/// has a typedef with a given name {@code Name}. -static bool hasTypedefNamed(QualType QT, - StringRef Name) { - while (auto *T = dyn_cast<TypedefType>(QT)) { - const auto &Context = T->getDecl()->getASTContext(); - if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name)) - return true; - QT = T->getDecl()->getUnderlyingType(); - } - return false; -} - -static QualType getCallableReturnType(const NamedDecl *ND) { - if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { - return FD->getReturnType(); - } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) { - return MD->getReturnType(); - } else { - llvm_unreachable("Unexpected decl"); - } -} - -bool RetainSummaryManager::applyParamAnnotationEffect( - const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD, - RetainSummaryTemplate &Template) { - QualType QT = pd->getType(); - if (auto K = - hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr, - GeneralizedConsumedAttr>(pd, QT)) { - Template->addArg(AF, parm_idx, ArgEffect(DecRef, *K)); - return true; - } else if (auto K = hasAnyEnabledAttrOf< - CFReturnsRetainedAttr, OSReturnsRetainedAttr, - OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr, - GeneralizedReturnsRetainedAttr>(pd, QT)) { - - // For OSObjects, we try to guess whether the object is created based - // on the return value. - if (K == ObjKind::OS) { - QualType QT = getCallableReturnType(FD); - - bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>(); - bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>(); - - // The usual convention is to create an object on non-zero return, but - // it's reverted if the typedef chain has a typedef kern_return_t, - // because kReturnSuccess constant is defined as zero. - // The convention can be overwritten by custom attributes. - bool SuccessOnZero = - HasRetainedOnZero || - (hasTypedefNamed(QT, "kern_return_t") && !HasRetainedOnNonZero); - bool ShouldSplit = !QT.isNull() && !QT->isVoidType(); - ArgEffectKind AK = RetainedOutParameter; - if (ShouldSplit && SuccessOnZero) { - AK = RetainedOutParameterOnZero; - } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) { - AK = RetainedOutParameterOnNonZero; - } - Template->addArg(AF, parm_idx, ArgEffect(AK, ObjKind::OS)); - } - - // For others: - // Do nothing. Retained out parameters will either point to a +1 reference - // or NULL, but the way you check for failure differs depending on the - // API. Consequently, we don't have a good way to track them yet. - return true; - } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr, - OSReturnsNotRetainedAttr, - GeneralizedReturnsNotRetainedAttr>( - pd, QT)) { - Template->addArg(AF, parm_idx, ArgEffect(UnretainedOutParameter, *K)); - return true; - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - for (const auto *OD : MD->overridden_methods()) { - const ParmVarDecl *OP = OD->parameters()[parm_idx]; - if (applyParamAnnotationEffect(OP, parm_idx, OD, Template)) - return true; - } - } - - return false; -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, - const FunctionDecl *FD) { - if (!FD) - return; - - assert(Summ && "Must have a summary to add annotations to."); - RetainSummaryTemplate Template(Summ, *this); - - // Effects on the parameters. - unsigned parm_idx = 0; - for (auto pi = FD->param_begin(), - pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) - applyParamAnnotationEffect(*pi, parm_idx, FD, Template); - - QualType RetTy = FD->getReturnType(); - if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD)) - Template->setRetEffect(*RetE); - - if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(FD, RetTy)) - Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS)); -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, - const ObjCMethodDecl *MD) { - if (!MD) - return; - - assert(Summ && "Must have a valid summary to add annotations to"); - RetainSummaryTemplate Template(Summ, *this); - - // Effects on the receiver. - if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(MD, MD->getReturnType())) - Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC)); - - // Effects on the parameters. - unsigned parm_idx = 0; - for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe; - ++pi, ++parm_idx) - applyParamAnnotationEffect(*pi, parm_idx, MD, Template); - - QualType RetTy = MD->getReturnType(); - if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD)) - Template->setRetEffect(*RetE); -} - -const RetainSummary * -RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, - Selector S, QualType RetTy) { - // Any special effects? - ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC); - RetEffect ResultEff = RetEffect::MakeNoRet(); - - // Check the method family, and apply any default annotations. - switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) { - case OMF_None: - case OMF_initialize: - case OMF_performSelector: - // Assume all Objective-C methods follow Cocoa Memory Management rules. - // FIXME: Does the non-threaded performSelector family really belong here? - // The selector could be, say, @selector(copy). - if (cocoa::isCocoaObjectRef(RetTy)) - ResultEff = RetEffect::MakeNotOwned(ObjKind::ObjC); - else if (coreFoundation::isCFObjectRef(RetTy)) { - // ObjCMethodDecl currently doesn't consider CF objects as valid return - // values for alloc, new, copy, or mutableCopy, so we have to - // double-check with the selector. This is ugly, but there aren't that - // many Objective-C methods that return CF objects, right? - if (MD) { - switch (S.getMethodFamily()) { - case OMF_alloc: - case OMF_new: - case OMF_copy: - case OMF_mutableCopy: - ResultEff = RetEffect::MakeOwned(ObjKind::CF); - break; - default: - ResultEff = RetEffect::MakeNotOwned(ObjKind::CF); - break; - } - } else { - ResultEff = RetEffect::MakeNotOwned(ObjKind::CF); - } - } - break; - case OMF_init: - ResultEff = ObjCInitRetE; - ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC); - break; - case OMF_alloc: - case OMF_new: - case OMF_copy: - case OMF_mutableCopy: - if (cocoa::isCocoaObjectRef(RetTy)) - ResultEff = ObjCAllocRetE; - else if (coreFoundation::isCFObjectRef(RetTy)) - ResultEff = RetEffect::MakeOwned(ObjKind::CF); - break; - case OMF_autorelease: - ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC); - break; - case OMF_retain: - ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC); - break; - case OMF_release: - ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC); - break; - case OMF_dealloc: - ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC); - break; - case OMF_self: - // -self is handled specially by the ExprEngine to propagate the receiver. - break; - case OMF_retainCount: - case OMF_finalize: - // These methods don't return objects. - break; - } - - // If one of the arguments in the selector has the keyword 'delegate' we - // should stop tracking the reference count for the receiver. This is - // because the reference count is quite possibly handled by a delegate - // method. - if (S.isKeywordSelector()) { - for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) { - StringRef Slot = S.getNameForSlot(i); - if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) { - if (ResultEff == ObjCInitRetE) - ResultEff = RetEffect::MakeNoRetHard(); - else - ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC); - } - } - } - - if (ReceiverEff.getKind() == DoNothing && - ResultEff.getKind() == RetEffect::NoRet) - return getDefaultSummary(); - - return getPersistentSummary(ResultEff, ArgEffects(AF.getEmptyMap()), - ArgEffect(ReceiverEff), ArgEffect(MayEscape)); -} - -const RetainSummary *RetainSummaryManager::getInstanceMethodSummary( - const ObjCMethodCall &Msg, - QualType ReceiverType) { - const ObjCInterfaceDecl *ReceiverClass = nullptr; - - // We do better tracking of the type of the object than the core ExprEngine. - // See if we have its type in our private state. - if (!ReceiverType.isNull()) - if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>()) - ReceiverClass = PT->getInterfaceDecl(); - - // If we don't know what kind of object this is, fall back to its static type. - if (!ReceiverClass) - ReceiverClass = Msg.getReceiverInterface(); - - // FIXME: The receiver could be a reference to a class, meaning that - // we should use the class method. - // id x = [NSObject class]; - // [x performSelector:... withObject:... afterDelay:...]; - Selector S = Msg.getSelector(); - const ObjCMethodDecl *Method = Msg.getDecl(); - if (!Method && ReceiverClass) - Method = ReceiverClass->getInstanceMethod(S); - - return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(), - ObjCMethodSummaries); -} - -const RetainSummary * -RetainSummaryManager::getMethodSummary(Selector S, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, QualType RetTy, - ObjCMethodSummariesTy &CachedSummaries) { - - // Objective-C method summaries are only applicable to ObjC and CF objects. - if (!TrackObjCAndCFObjects) - return getDefaultSummary(); - - // Look up a summary in our summary cache. - const RetainSummary *Summ = CachedSummaries.find(ID, S); - - if (!Summ) { - Summ = getStandardMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(Summ, MD); - - // Memoize the summary. - CachedSummaries[ObjCSummaryKey(ID, S)] = Summ; - } - - return Summ; -} - -void RetainSummaryManager::InitializeClassMethodSummaries() { - ArgEffects ScratchArgs = AF.getEmptyMap(); - - // Create the [NSAssertionHandler currentHander] summary. - addClassMethSummary("NSAssertionHandler", "currentHandler", - getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::ObjC), - ScratchArgs)); - - // Create the [NSAutoreleasePool addObject:] summary. - ScratchArgs = AF.add(ScratchArgs, 0, ArgEffect(Autorelease)); - addClassMethSummary("NSAutoreleasePool", "addObject", - getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(DoNothing), - ArgEffect(Autorelease))); -} - -void RetainSummaryManager::InitializeMethodSummaries() { - - ArgEffects ScratchArgs = AF.getEmptyMap(); - // Create the "init" selector. It just acts as a pass-through for the - // receiver. - const RetainSummary *InitSumm = getPersistentSummary( - ObjCInitRetE, ScratchArgs, ArgEffect(DecRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); - - // awakeAfterUsingCoder: behaves basically like an 'init' method. It - // claims the receiver and returns a retained object. - addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), - InitSumm); - - // The next methods are allocators. - const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE, - ScratchArgs); - const RetainSummary *CFAllocSumm = - getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs); - - // Create the "retain" selector. - RetEffect NoRet = RetEffect::MakeNoRet(); - const RetainSummary *Summ = getPersistentSummary( - NoRet, ScratchArgs, ArgEffect(IncRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); - - // Create the "release" selector. - Summ = getPersistentSummary(NoRet, ScratchArgs, - ArgEffect(DecRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - - // Create the -dealloc summary. - Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Dealloc, - ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); - - // Create the "autorelease" selector. - Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Autorelease, - ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - - // For NSWindow, allocated objects are (initially) self-owned. - // FIXME: For now we opt for false negatives with NSWindow, as these objects - // self-own themselves. However, they only do this once they are displayed. - // Thus, we need to track an NSWindow's display status. - // This is tracked in <rdar://problem/6062711>. - // See also http://llvm.org/bugs/show_bug.cgi?id=3714. - const RetainSummary *NoTrackYet = - getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(StopTracking), ArgEffect(StopTracking)); - - addClassMethSummary("NSWindow", "alloc", NoTrackYet); - - // For NSPanel (which subclasses NSWindow), allocated objects are not - // self-owned. - // FIXME: For now we don't track NSPanels. object for the same reason - // as for NSWindow objects. - addClassMethSummary("NSPanel", "alloc", NoTrackYet); - - // For NSNull, objects returned by +null are singletons that ignore - // retain/release semantics. Just don't track them. - // <rdar://problem/12858915> - addClassMethSummary("NSNull", "null", NoTrackYet); - - // Don't track allocated autorelease pools, as it is okay to prematurely - // exit a method. - addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); - addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false); - addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet); - - // Create summaries QCRenderer/QCView -createSnapShotImageOfType: - addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType"); - addInstMethSummary("QCView", AllocSumm, "createSnapshotImageOfType"); - - // Create summaries for CIContext, 'createCGImage' and - // 'createCGLayerWithSize'. These objects are CF objects, and are not - // automatically garbage collected. - addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect"); - addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect", - "format", "colorSpace"); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info"); -} - -CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { - ASTContext &Ctx = MD->getASTContext(); - LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, - /*TrackNSAndCFObjects=*/true, - /*TrackOSObjects=*/false); - const RetainSummary *S = M.getMethodSummary(MD); - CallEffects CE(S->getRetEffect(), S->getReceiverEffect()); - unsigned N = MD->param_size(); - for (unsigned i = 0; i < N; ++i) { - CE.Args.push_back(S->getArg(i)); - } - return CE; -} - -CallEffects CallEffects::getEffect(const FunctionDecl *FD) { - ASTContext &Ctx = FD->getASTContext(); - LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, - /*TrackNSAndCFObjects=*/true, - /*TrackOSObjects=*/false); - const RetainSummary *S = M.getFunctionSummary(FD); - CallEffects CE(S->getRetEffect()); - unsigned N = FD->param_size(); - for (unsigned i = 0; i < N; ++i) { - CE.Args.push_back(S->getArg(i)); - } - return CE; -} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp new file mode 100644 index 000000000000..d5c14351d330 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp @@ -0,0 +1,18 @@ +//== SMTConstraintManager.cpp -----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" + +using namespace clang; +using namespace ento; + +std::unique_ptr<ConstraintManager> +ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { + return llvm::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index 6c0d487c8a87..3a5841137e1a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -1,9 +1,8 @@ //===- SValBuilder.cpp - Basic class for all SValBuilder implementations --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp index 933c5c330072..9b5de6c3eb92 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -1,9 +1,8 @@ -//===- RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -------===// +//===-- SVals.cpp - Abstract RValues for Path-Sens. Value Tracking --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -17,6 +16,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -284,6 +284,15 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(llvm::errs()); } +void SVal::printJson(raw_ostream &Out, bool AddQuotes) const { + std::string Buf; + llvm::raw_string_ostream TempOut(Buf); + + dumpToStream(TempOut); + + Out << JsonFormat(TempOut.str(), AddQuotes); +} + void SVal::dumpToStream(raw_ostream &os) const { switch (getBaseKind()) { case UnknownValKind: diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index fecbc0001079..d1faf3f4dea9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -1,9 +1,8 @@ //===--- SarifDiagnostics.cpp - Sarif Diagnostics for Paths -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -192,15 +191,16 @@ static json::Object createLocation(json::Object &&PhysicalLocation, static Importance calculateImportance(const PathDiagnosticPiece &Piece) { switch (Piece.getKind()) { - case PathDiagnosticPiece::Kind::Call: - case PathDiagnosticPiece::Kind::Macro: - case PathDiagnosticPiece::Kind::Note: + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::Macro: + case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: // FIXME: What should be reported here? break; - case PathDiagnosticPiece::Kind::Event: + case PathDiagnosticPiece::Event: return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important : Importance::Essential; - case PathDiagnosticPiece::Kind::ControlFlow: + case PathDiagnosticPiece::ControlFlow: return Importance::Unimportant; } return Importance::Unimportant; @@ -257,7 +257,7 @@ static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, static StringRef getRuleDescription(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ .Case(FULLNAME, HELPTEXT) #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -268,7 +268,7 @@ static StringRef getRuleDescription(StringRef CheckName) { static StringRef getRuleHelpURIStr(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ .Case(FULLNAME, DOC_URI) #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -345,5 +345,5 @@ void SarifDiagnostics::FlushDiagnosticsImpl( "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"}, {"version", "2.0.0-csd.2.beta.2018-11-28"}, {"runs", json::Array{createRun(Diags)}}}; - OS << llvm::formatv("{0:2}", json::Value(std::move(Sarif))); + OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif))); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index adb40178f5b1..85f60231a276 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -1,9 +1,8 @@ //== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index fc57cecac9cb..84c52f53ca5e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -1,9 +1,8 @@ // SimpleSValBuilder.cpp - A basic SValBuilder -----------------------*- C++ -*- // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -526,7 +525,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_Sub: if (resultTy->isIntegralOrEnumerationType()) return makeIntVal(0, resultTy); - return evalCastFromNonLoc(makeIntVal(0, /*Unsigned=*/false), resultTy); + return evalCastFromNonLoc(makeIntVal(0, /*isUnsigned=*/false), resultTy); case BO_Or: case BO_And: return evalCastFromNonLoc(lhs, resultTy); @@ -572,7 +571,15 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, // then pack it back into a LocAsInteger. llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); - BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); + // If the region has a symbolic base, pay attention to the type; it + // might be coming from a non-default address space. For non-symbolic + // regions it doesn't matter that much because such comparisons would + // most likely evaluate to concrete false anyway. FIXME: We might + // still need to handle the non-comparison case. + if (SymbolRef lSym = lhs.getAsLocSymbol(true)) + BasicVals.getAPSIntType(lSym->getType()).apply(i); + else + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } default: diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp index 4fa937d9658d..3cf616161c66 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -1,9 +1,8 @@ //===- Store.cpp - Interface for maps from Locations to Values ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp index 350f4b8bb3a2..d7ddd9cf4610 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp @@ -1,9 +1,8 @@ //== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index 66273f099a38..675209f6fd7e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -1,9 +1,8 @@ //===- SymbolManager.h - Management of Symbolic Values --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -405,7 +404,7 @@ void SymbolReaper::markLive(SymbolRef sym) { } void SymbolReaper::markLive(const MemRegion *region) { - RegionRoots.insert(region); + RegionRoots.insert(region->getBaseRegion()); markElementIndicesLive(region); } @@ -426,11 +425,15 @@ void SymbolReaper::markInUse(SymbolRef sym) { } bool SymbolReaper::isLiveRegion(const MemRegion *MR) { + // TODO: For now, liveness of a memory region is equivalent to liveness of its + // base region. In fact we can do a bit better: say, if a particular FieldDecl + // is not used later in the path, we can diagnose a leak of a value within + // that field earlier than, say, the variable that contains the field dies. + MR = MR->getBaseRegion(); + if (RegionRoots.count(MR)) return true; - MR = MR->getBaseRegion(); - if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) return isLive(SR->getSymbol()); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp deleted file mode 100644 index c34b0ca1839d..000000000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//== TaintManager.cpp ------------------------------------------ -*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" - -using namespace clang; -using namespace ento; - -void *ProgramStateTrait<TaintMap>::GDMIndex() { - static int index = 0; - return &index; -} - -void *ProgramStateTrait<DerivedSymTaint>::GDMIndex() { - static int index; - return &index; -} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp index e705393cb83a..129d1720395e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp @@ -1,9 +1,8 @@ //===- WorkList.cpp - Analyzer work-list implementation--------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp deleted file mode 100644 index c4729f969f33..000000000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp +++ /dev/null @@ -1,841 +0,0 @@ -//== Z3ConstraintManager.cpp --------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" - -#include "clang/Config/config.h" - -using namespace clang; -using namespace ento; - -#if CLANG_ANALYZER_WITH_Z3 - -#include <z3.h> - -namespace { - -/// Configuration class for Z3 -class Z3Config { - friend class Z3Context; - - Z3_config Config; - -public: - Z3Config() : Config(Z3_mk_config()) { - // Enable model finding - Z3_set_param_value(Config, "model", "true"); - // Disable proof generation - Z3_set_param_value(Config, "proof", "false"); - // Set timeout to 15000ms = 15s - Z3_set_param_value(Config, "timeout", "15000"); - } - - ~Z3Config() { Z3_del_config(Config); } -}; // end class Z3Config - -// Function used to report errors -void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) { - llvm::report_fatal_error("Z3 error: " + - llvm::Twine(Z3_get_error_msg(Context, Error))); -} - -/// Wrapper for Z3 context -class Z3Context { -public: - Z3_context Context; - - Z3Context() { - Context = Z3_mk_context_rc(Z3Config().Config); - // The error function is set here because the context is the first object - // created by the backend - Z3_set_error_handler(Context, Z3ErrorHandler); - } - - virtual ~Z3Context() { - Z3_del_context(Context); - Context = nullptr; - } -}; // end class Z3Context - -/// Wrapper for Z3 Sort -class Z3Sort : public SMTSort { - friend class Z3Solver; - - Z3Context &Context; - - Z3_sort Sort; - -public: - /// Default constructor, mainly used by make_shared - Z3Sort(Z3Context &C, Z3_sort ZS) : Context(C), Sort(ZS) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - /// Override implicit copy constructor for correct reference counting. - Z3Sort(const Z3Sort &Other) : Context(Other.Context), Sort(Other.Sort) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - /// Override implicit copy assignment constructor for correct reference - /// counting. - Z3Sort &operator=(const Z3Sort &Other) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Other.Sort)); - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - Sort = Other.Sort; - return *this; - } - - Z3Sort(Z3Sort &&Other) = delete; - Z3Sort &operator=(Z3Sort &&Other) = delete; - - ~Z3Sort() { - if (Sort) - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - bool isBitvectorSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BV_SORT); - } - - bool isFloatSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_FLOATING_POINT_SORT); - } - - bool isBooleanSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BOOL_SORT); - } - - unsigned getBitvectorSortSizeImpl() const override { - return Z3_get_bv_sort_size(Context.Context, Sort); - } - - unsigned getFloatSortSizeImpl() const override { - return Z3_fpa_get_ebits(Context.Context, Sort) + - Z3_fpa_get_sbits(Context.Context, Sort); - } - - bool equal_to(SMTSort const &Other) const override { - return Z3_is_eq_sort(Context.Context, Sort, - static_cast<const Z3Sort &>(Other).Sort); - } - - void print(raw_ostream &OS) const override { - OS << Z3_sort_to_string(Context.Context, Sort); - } -}; // end class Z3Sort - -static const Z3Sort &toZ3Sort(const SMTSort &S) { - return static_cast<const Z3Sort &>(S); -} - -class Z3Expr : public SMTExpr { - friend class Z3Solver; - - Z3Context &Context; - - Z3_ast AST; - -public: - Z3Expr(Z3Context &C, Z3_ast ZA) : SMTExpr(), Context(C), AST(ZA) { - Z3_inc_ref(Context.Context, AST); - } - - /// Override implicit copy constructor for correct reference counting. - Z3Expr(const Z3Expr &Copy) : SMTExpr(), Context(Copy.Context), AST(Copy.AST) { - Z3_inc_ref(Context.Context, AST); - } - - /// Override implicit copy assignment constructor for correct reference - /// counting. - Z3Expr &operator=(const Z3Expr &Other) { - Z3_inc_ref(Context.Context, Other.AST); - Z3_dec_ref(Context.Context, AST); - AST = Other.AST; - return *this; - } - - Z3Expr(Z3Expr &&Other) = delete; - Z3Expr &operator=(Z3Expr &&Other) = delete; - - ~Z3Expr() { - if (AST) - Z3_dec_ref(Context.Context, AST); - } - - void Profile(llvm::FoldingSetNodeID &ID) const override { - ID.AddInteger(Z3_get_ast_hash(Context.Context, AST)); - } - - /// Comparison of AST equality, not model equivalence. - bool equal_to(SMTExpr const &Other) const override { - assert(Z3_is_eq_sort(Context.Context, Z3_get_sort(Context.Context, AST), - Z3_get_sort(Context.Context, - static_cast<const Z3Expr &>(Other).AST)) && - "AST's must have the same sort"); - return Z3_is_eq_ast(Context.Context, AST, - static_cast<const Z3Expr &>(Other).AST); - } - - void print(raw_ostream &OS) const override { - OS << Z3_ast_to_string(Context.Context, AST); - } -}; // end class Z3Expr - -static const Z3Expr &toZ3Expr(const SMTExpr &E) { - return static_cast<const Z3Expr &>(E); -} - -class Z3Model { - friend class Z3Solver; - - Z3Context &Context; - - Z3_model Model; - -public: - Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) { - Z3_model_inc_ref(Context.Context, Model); - } - - Z3Model(const Z3Model &Other) = delete; - Z3Model(Z3Model &&Other) = delete; - Z3Model &operator=(Z3Model &Other) = delete; - Z3Model &operator=(Z3Model &&Other) = delete; - - ~Z3Model() { - if (Model) - Z3_model_dec_ref(Context.Context, Model); - } - - void print(raw_ostream &OS) const { - OS << Z3_model_to_string(Context.Context, Model); - } - - LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } -}; // end class Z3Model - -/// Get the corresponding IEEE floating-point type for a given bitwidth. -static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) { - switch (BitWidth) { - default: - llvm_unreachable("Unsupported floating-point semantics!"); - break; - case 16: - return llvm::APFloat::IEEEhalf(); - case 32: - return llvm::APFloat::IEEEsingle(); - case 64: - return llvm::APFloat::IEEEdouble(); - case 128: - return llvm::APFloat::IEEEquad(); - } -} - -// Determine whether two float semantics are equivalent -static bool areEquivalent(const llvm::fltSemantics &LHS, - const llvm::fltSemantics &RHS) { - return (llvm::APFloat::semanticsPrecision(LHS) == - llvm::APFloat::semanticsPrecision(RHS)) && - (llvm::APFloat::semanticsMinExponent(LHS) == - llvm::APFloat::semanticsMinExponent(RHS)) && - (llvm::APFloat::semanticsMaxExponent(LHS) == - llvm::APFloat::semanticsMaxExponent(RHS)) && - (llvm::APFloat::semanticsSizeInBits(LHS) == - llvm::APFloat::semanticsSizeInBits(RHS)); -} - -} // end anonymous namespace - -typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty; -REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, ConstraintZ3Ty) - -namespace { - -class Z3Solver : public SMTSolver { - friend class Z3ConstraintManager; - - Z3Context Context; - - Z3_solver Solver; - -public: - Z3Solver() : Solver(Z3_mk_simple_solver(Context.Context)) { - Z3_solver_inc_ref(Context.Context, Solver); - } - - Z3Solver(const Z3Solver &Other) = delete; - Z3Solver(Z3Solver &&Other) = delete; - Z3Solver &operator=(Z3Solver &Other) = delete; - Z3Solver &operator=(Z3Solver &&Other) = delete; - - ~Z3Solver() { - if (Solver) - Z3_solver_dec_ref(Context.Context, Solver); - } - - void addConstraint(const SMTExprRef &Exp) const override { - Z3_solver_assert(Context.Context, Solver, toZ3Expr(*Exp).AST); - } - - SMTSortRef getBoolSort() override { - return std::make_shared<Z3Sort>(Context, Z3_mk_bool_sort(Context.Context)); - } - - SMTSortRef getBitvectorSort(unsigned BitWidth) override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_bv_sort(Context.Context, BitWidth)); - } - - SMTSortRef getSort(const SMTExprRef &Exp) override { - return std::make_shared<Z3Sort>( - Context, Z3_get_sort(Context.Context, toZ3Expr(*Exp).AST)); - } - - SMTSortRef getFloat16Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_16(Context.Context)); - } - - SMTSortRef getFloat32Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_32(Context.Context)); - } - - SMTSortRef getFloat64Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_64(Context.Context)); - } - - SMTSortRef getFloat128Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_128(Context.Context)); - } - - SMTExprRef newExprRef(const SMTExpr &E) const override { - return std::make_shared<Z3Expr>(toZ3Expr(E)); - } - - SMTExprRef mkBVNeg(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvneg(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVNot(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvnot(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkNot(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_not(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvadd(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvmul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsrem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvurem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsdiv(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvudiv(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvshl(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvashr(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvlshr(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvxor(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvor(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvand(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvult(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvslt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvugt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsgt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvule(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsle(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvuge(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsge(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST}; - return newExprRef(Z3Expr(Context, Z3_mk_and(Context.Context, 2, Args))); - } - - SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST}; - return newExprRef(Z3Expr(Context, Z3_mk_or(Context.Context, 2, Args))); - } - - SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_eq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPNeg(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_neg(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_infinite(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_is_nan(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_normal(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsZero(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_zero(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_mul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_div(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_rem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_add(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_sub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_lt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_gt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_leq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_geq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_eq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T, - const SMTExprRef &F) override { - return newExprRef( - Z3Expr(Context, Z3_mk_ite(Context.Context, toZ3Expr(*Cond).AST, - toZ3Expr(*T).AST, toZ3Expr(*F).AST))); - } - - SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_sign_ext(Context.Context, i, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_zero_ext(Context.Context, i, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVExtract(unsigned High, unsigned Low, - const SMTExprRef &Exp) override { - return newExprRef(Z3Expr(Context, Z3_mk_extract(Context.Context, High, Low, - toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVConcat(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_concat(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_float(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkSBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_signed(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkUBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_unsigned(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_sbv(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, ToWidth))); - } - - SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_ubv(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, ToWidth))); - } - - SMTExprRef mkBoolean(const bool b) override { - return newExprRef(Z3Expr(Context, b ? Z3_mk_true(Context.Context) - : Z3_mk_false(Context.Context))); - } - - SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) override { - const SMTSortRef Sort = getBitvectorSort(BitWidth); - return newExprRef( - Z3Expr(Context, Z3_mk_numeral(Context.Context, Int.toString(10).c_str(), - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef mkFloat(const llvm::APFloat Float) override { - SMTSortRef Sort = - getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); - - llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false); - SMTExprRef Z3Int = mkBitvector(Int, Int.getBitWidth()); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST, - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) override { - return newExprRef( - Z3Expr(Context, Z3_mk_const(Context.Context, - Z3_mk_string_symbol(Context.Context, Name), - toZ3Sort(*Sort).Sort))); - } - - llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, - bool isUnsigned) override { - return llvm::APSInt( - llvm::APInt(BitWidth, - Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST), - 10), - isUnsigned); - } - - bool getBoolean(const SMTExprRef &Exp) override { - return Z3_get_bool_value(Context.Context, toZ3Expr(*Exp).AST) == Z3_L_TRUE; - } - - SMTExprRef getFloatRoundingMode() override { - // TODO: Don't assume nearest ties to even rounding mode - return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context))); - } - - bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST, - llvm::APFloat &Float, bool useSemantics) { - assert(Sort->isFloatSort() && "Unsupported sort to floating-point!"); - - llvm::APSInt Int(Sort->getFloatSortSize(), true); - const llvm::fltSemantics &Semantics = - getFloatSemantics(Sort->getFloatSortSize()); - SMTSortRef BVSort = getBitvectorSort(Sort->getFloatSortSize()); - if (!toAPSInt(BVSort, AST, Int, true)) { - return false; - } - - if (useSemantics && !areEquivalent(Float.getSemantics(), Semantics)) { - assert(false && "Floating-point types don't match!"); - return false; - } - - Float = llvm::APFloat(Semantics, Int); - return true; - } - - bool toAPSInt(const SMTSortRef &Sort, const SMTExprRef &AST, - llvm::APSInt &Int, bool useSemantics) { - if (Sort->isBitvectorSort()) { - if (useSemantics && Int.getBitWidth() != Sort->getBitvectorSortSize()) { - assert(false && "Bitvector types don't match!"); - return false; - } - - // FIXME: This function is also used to retrieve floating-point values, - // which can be 16, 32, 64 or 128 bits long. Bitvectors can be anything - // between 1 and 64 bits long, which is the reason we have this weird - // guard. In the future, we need proper calls in the backend to retrieve - // floating-points and its special values (NaN, +/-infinity, +/-zero), - // then we can drop this weird condition. - if (Sort->getBitvectorSortSize() <= 64 || - Sort->getBitvectorSortSize() == 128) { - Int = getBitvector(AST, Int.getBitWidth(), Int.isUnsigned()); - return true; - } - - assert(false && "Bitwidth not supported!"); - return false; - } - - if (Sort->isBooleanSort()) { - if (useSemantics && Int.getBitWidth() < 1) { - assert(false && "Boolean type doesn't match!"); - return false; - } - - Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), getBoolean(AST)), - Int.isUnsigned()); - return true; - } - - llvm_unreachable("Unsupported sort to integer!"); - } - - bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override { - Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); - Z3_func_decl Func = Z3_get_app_decl( - Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); - if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) - return false; - - SMTExprRef Assign = newExprRef( - Z3Expr(Context, - Z3_model_get_const_interp(Context.Context, Model.Model, Func))); - SMTSortRef Sort = getSort(Assign); - return toAPSInt(Sort, Assign, Int, true); - } - - bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override { - Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); - Z3_func_decl Func = Z3_get_app_decl( - Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); - if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) - return false; - - SMTExprRef Assign = newExprRef( - Z3Expr(Context, - Z3_model_get_const_interp(Context.Context, Model.Model, Func))); - SMTSortRef Sort = getSort(Assign); - return toAPFloat(Sort, Assign, Float, true); - } - - Optional<bool> check() const override { - Z3_lbool res = Z3_solver_check(Context.Context, Solver); - if (res == Z3_L_TRUE) - return true; - - if (res == Z3_L_FALSE) - return false; - - return Optional<bool>(); - } - - void push() override { return Z3_solver_push(Context.Context, Solver); } - - void pop(unsigned NumStates = 1) override { - assert(Z3_solver_get_num_scopes(Context.Context, Solver) >= NumStates); - return Z3_solver_pop(Context.Context, Solver, NumStates); - } - - bool isFPSupported() override { return true; } - - /// Reset the solver and remove all constraints. - void reset() override { Z3_solver_reset(Context.Context, Solver); } - - void print(raw_ostream &OS) const override { - OS << Z3_solver_to_string(Context.Context, Solver); - } -}; // end class Z3Solver - -class Z3ConstraintManager : public SMTConstraintManager<ConstraintZ3, Z3Expr> { - SMTSolverRef Solver = CreateZ3Solver(); - -public: - Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) - : SMTConstraintManager(SE, SB, Solver) {} -}; // end class Z3ConstraintManager - -} // end anonymous namespace - -#endif - -SMTSolverRef clang::ento::CreateZ3Solver() { -#if CLANG_ANALYZER_WITH_Z3 - return llvm::make_unique<Z3Solver>(); -#else - llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", - false); - return nullptr; -#endif -} - -std::unique_ptr<ConstraintManager> -ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { -#if CLANG_ANALYZER_WITH_Z3 - return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder()); -#else - llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", - false); - return nullptr; -#endif -} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index d87937d9b63d..454b61fd51a1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -1,9 +1,8 @@ //===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -84,10 +83,11 @@ void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, namespace { class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { DiagnosticsEngine &Diag; - bool IncludePath; + bool IncludePath, ShouldEmitAsError; + public: ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) - : Diag(Diag), IncludePath(false) {} + : Diag(Diag), IncludePath(false), ShouldEmitAsError(false) {} ~ClangDiagPathDiagConsumer() override {} StringRef getName() const override { return "ClangDiags"; } @@ -102,9 +102,14 @@ public: IncludePath = true; } + void enableWerror() { ShouldEmitAsError = true; } + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) override { - unsigned WarnID = Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); + unsigned WarnID = + ShouldEmitAsError + ? Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0") + : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0"); for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(), @@ -191,7 +196,9 @@ public: /// Time the analyzes time of each translation unit. std::unique_ptr<llvm::TimerGroup> AnalyzerTimers; - std::unique_ptr<llvm::Timer> TUTotalTimer; + std::unique_ptr<llvm::Timer> SyntaxCheckTimer; + std::unique_ptr<llvm::Timer> ExprEngineTimer; + std::unique_ptr<llvm::Timer> BugReporterTimer; /// The information about analyzed functions shared throughout the /// translation unit. @@ -207,8 +214,13 @@ public: if (Opts->PrintStats || Opts->ShouldSerializeStats) { AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>( "analyzer", "Analyzer timers"); - TUTotalTimer = llvm::make_unique<llvm::Timer>( - "time", "Analyzer total time", *AnalyzerTimers); + SyntaxCheckTimer = llvm::make_unique<llvm::Timer>( + "syntaxchecks", "Syntax-based analysis time", *AnalyzerTimers); + ExprEngineTimer = llvm::make_unique<llvm::Timer>( + "exprengine", "Path exploration time", *AnalyzerTimers); + BugReporterTimer = llvm::make_unique<llvm::Timer>( + "bugreporter", "Path-sensitive report post-processing time", + *AnalyzerTimers); llvm::EnableStatistics(/* PrintOnExit= */ false); } } @@ -226,6 +238,9 @@ public: new ClangDiagPathDiagConsumer(PP.getDiagnostics()); PathConsumers.push_back(clangDiags); + if (Opts->AnalyzerWerror) + clangDiags->enableWerror(); + if (Opts->AnalysisDiagOpt == PD_TEXT) { clangDiags->enablePaths(); @@ -338,8 +353,42 @@ public: /// Handle callbacks for arbitrary Decls. bool VisitDecl(Decl *D) { AnalysisMode Mode = getModeForDecl(D, RecVisitorMode); - if (Mode & AM_Syntax) + if (Mode & AM_Syntax) { + if (SyntaxCheckTimer) + SyntaxCheckTimer->startTimer(); checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR); + if (SyntaxCheckTimer) + SyntaxCheckTimer->stopTimer(); + } + return true; + } + + bool VisitVarDecl(VarDecl *VD) { + if (!Opts->IsNaiveCTUEnabled) + return true; + + if (VD->hasExternalStorage() || VD->isStaticDataMember()) { + if (!cross_tu::containsConst(VD, *Ctx)) + return true; + } else { + // Cannot be initialized in another TU. + return true; + } + + if (VD->getAnyInitializer()) + return true; + + llvm::Expected<const VarDecl *> CTUDeclOrError = + CTU.getCrossTUDefinition(VD, Opts->CTUDir, Opts->CTUIndexName, + Opts->DisplayCTUProgress); + + if (!CTUDeclOrError) { + handleAllErrors(CTUDeclOrError.takeError(), + [&](const cross_tu::IndexError &IE) { + CTU.emitCrossTUDiagnostics(IE); + }); + } + return true; } @@ -529,7 +578,11 @@ static bool isBisonFile(ASTContext &C) { void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) { BugReporter BR(*Mgr); TranslationUnitDecl *TU = C.getTranslationUnitDecl(); + if (SyntaxCheckTimer) + SyntaxCheckTimer->startTimer(); checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); + if (SyntaxCheckTimer) + SyntaxCheckTimer->stopTimer(); // Run the AST-only checks using the order in which functions are defined. // If inlining is not turned on, use the simplest function order for path @@ -571,8 +624,6 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) return; - if (TUTotalTimer) TUTotalTimer->startTimer(); - if (isBisonFile(C)) { reportAnalyzerProgress("Skipping bison-generated file\n"); } else if (Opts->DisableAllChecks) { @@ -585,8 +636,6 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { runAnalysisOnTranslationUnit(C); } - if (TUTotalTimer) TUTotalTimer->stopTimer(); - // Count how many basic blocks we have not covered. NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks(); NumVisitedBlocksInAnalyzedFunctions = @@ -710,8 +759,13 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, BugReporter BR(*Mgr); - if (Mode & AM_Syntax) + if (Mode & AM_Syntax) { + if (SyntaxCheckTimer) + SyntaxCheckTimer->startTimer(); checkerMgr->runCheckersOnASTBody(D, *Mgr, BR); + if (SyntaxCheckTimer) + SyntaxCheckTimer->stopTimer(); + } if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) { RunPathSensitiveChecks(D, IMode, VisitedCallees); if (IMode != ExprEngine::Inline_Minimal) @@ -738,8 +792,12 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode); // Execute the worklist algorithm. + if (ExprEngineTimer) + ExprEngineTimer->startTimer(); Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), Mgr->options.MaxNodesPerTopLevelFunction); + if (ExprEngineTimer) + ExprEngineTimer->stopTimer(); if (!Mgr->options.DumpExplodedGraphTo.empty()) Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo); @@ -749,7 +807,11 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, Eng.ViewGraph(Mgr->options.TrimGraph); // Display warnings. + if (BugReporterTimer) + BugReporterTimer->startTimer(); Eng.getBugReporter().FlushReports(); + if (BugReporterTimer) + BugReporterTimer->stopTimer(); } //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index 1c31c35b75e4..1e45ee96145a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -1,9 +1,8 @@ //===--- CheckerRegistration.cpp - Registration for the Analyzer Checkers -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -19,7 +18,6 @@ #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" #include <memory> @@ -34,46 +32,52 @@ std::unique_ptr<CheckerManager> ento::createCheckerManager( DiagnosticsEngine &diags) { auto checkerMgr = llvm::make_unique<CheckerManager>(context, opts); - CheckerRegistry allCheckers(plugins, diags); - - for (const auto &Fn : checkerRegistrationFns) - Fn(allCheckers); + CheckerRegistry allCheckers(plugins, diags, opts, context.getLangOpts(), + checkerRegistrationFns); - allCheckers.initializeManager(*checkerMgr, opts); - allCheckers.validateCheckerOptions(opts); + allCheckers.initializeManager(*checkerMgr); + allCheckers.validateCheckerOptions(); checkerMgr->finishedCheckerRegistration(); return checkerMgr; } void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins, - DiagnosticsEngine &diags) { + AnalyzerOptions &anopts, + DiagnosticsEngine &diags, + const LangOptions &langOpts) { out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n"; out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n"; - CheckerRegistry(plugins, diags).printHelp(out); + CheckerRegistry(plugins, diags, anopts, langOpts) + .printCheckerWithDescList(out); } void ento::printEnabledCheckerList(raw_ostream &out, ArrayRef<std::string> plugins, - const AnalyzerOptions &opts, - DiagnosticsEngine &diags) { + AnalyzerOptions &anopts, + DiagnosticsEngine &diags, + const LangOptions &langOpts) { out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; - CheckerRegistry(plugins, diags).printList(out, opts); + CheckerRegistry(plugins, diags, anopts, langOpts) + .printEnabledCheckerList(out); +} + +void ento::printCheckerConfigList(raw_ostream &OS, + ArrayRef<std::string> plugins, + AnalyzerOptions &opts, + DiagnosticsEngine &diags, + const LangOptions &LangOpts) { + CheckerRegistry(plugins, diags, opts, LangOpts) + .printCheckerOptionList(OS); } void ento::printAnalyzerConfigList(raw_ostream &out) { out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n"; - out << "USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config " - "<OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; - out << " clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, " - "-analyzer-config OPTION2=VALUE, ...\n\n"; - out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang" - "<OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; - out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang " - "OPTION1=VALUE, -Xclang -analyzer-config -Xclang " - "OPTION2=VALUE, ...\n\n"; + out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; + out << " -analyzer-config OPTION1=VALUE, -analyzer-config " + "OPTION2=VALUE, ...\n\n"; out << "OPTIONS:\n\n"; using OptionAndDescriptionTy = std::pair<StringRef, std::string>; @@ -107,31 +111,10 @@ void ento::printAnalyzerConfigList(raw_ostream &out) { return LHS.first < RHS.first; }); - constexpr size_t MinLineWidth = 70; - constexpr size_t PadForOpt = 2; - constexpr size_t OptionWidth = 30; - constexpr size_t PadForDesc = PadForOpt + OptionWidth; - static_assert(MinLineWidth > PadForDesc, "MinLineWidth must be greater!"); - - llvm::formatted_raw_ostream FOut(out); - for (const auto &Pair : PrintableOptions) { - FOut.PadToColumn(PadForOpt) << Pair.first; - - // If the buffer's length is greater then PadForDesc, print a newline. - if (FOut.getColumn() > PadForDesc) - FOut << '\n'; - - FOut.PadToColumn(PadForDesc); - - for (char C : Pair.second) { - if (FOut.getColumn() > MinLineWidth && C == ' ') { - FOut << '\n'; - FOut.PadToColumn(PadForDesc); - continue; - } - FOut << C; - } - FOut << "\n\n"; + AnalyzerOptions::printFormattedEntry(out, Pair, /*InitialPad*/ 2, + /*EntryWidth*/ 30, + /*MinLineWidth*/ 70); + out << "\n\n"; } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp index 620c0e588906..3fd4c36947cb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -1,19 +1,19 @@ //===- CheckerRegistry.cpp - Maintains all available checkers -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LLVM.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringMap.h" @@ -29,219 +29,592 @@ using llvm::sys::DynamicLibrary; using RegisterCheckersFn = void (*)(CheckerRegistry &); -static bool isCompatibleAPIVersion(const char *versionString) { - // If the version string is null, it's not an analyzer plugin. - if (!versionString) +static bool isCompatibleAPIVersion(const char *VersionString) { + // If the version string is null, its not an analyzer plugin. + if (!VersionString) return false; // For now, none of the static analyzer API is considered stable. // Versions must match exactly. - return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; + return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; +} + +namespace { +template <class T> struct FullNameLT { + bool operator()(const T &Lhs, const T &Rhs) { + return Lhs.FullName < Rhs.FullName; + } +}; + +using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>; +using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>; +} // end of anonymous namespace + +template <class CheckerOrPackageInfoList> +static + typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value, + typename CheckerOrPackageInfoList::const_iterator, + typename CheckerOrPackageInfoList::iterator>::type + binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) { + + using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type; + using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>; + + assert(std::is_sorted(Collection.begin(), Collection.end(), + CheckerOrPackageFullNameLT{}) && + "In order to efficiently gather checkers/packages, this function " + "expects them to be already sorted!"); + + return llvm::lower_bound(Collection, CheckerOrPackage(FullName), + CheckerOrPackageFullNameLT{}); +} + +static constexpr char PackageSeparator = '.'; + +static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker, + StringRef PackageName) { + // Does the checker's full name have the package as a prefix? + if (!Checker.FullName.startswith(PackageName)) + return false; + + // Is the package actually just the name of a specific checker? + if (Checker.FullName.size() == PackageName.size()) + return true; + + // Is the checker in the package (or a subpackage)? + if (Checker.FullName[PackageName.size()] == PackageSeparator) + return true; + + return false; } -CheckerRegistry::CheckerRegistry(ArrayRef<std::string> plugins, - DiagnosticsEngine &diags) : Diags(diags) { +CheckerRegistry::CheckerInfoListRange +CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) { + auto It = binaryFind(Checkers, CmdLineArg); + + if (!isInPackage(*It, CmdLineArg)) + return {Checkers.end(), Checkers.end()}; + + // See how large the package is. + // If the package doesn't exist, assume the option refers to a single + // checker. + size_t Size = 1; + llvm::StringMap<size_t>::const_iterator PackageSize = + PackageSizes.find(CmdLineArg); + + if (PackageSize != PackageSizes.end()) + Size = PackageSize->getValue(); + + return {It, It + Size}; +} + +CheckerRegistry::CheckerRegistry( + ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags, + AnalyzerOptions &AnOpts, const LangOptions &LangOpts, + ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns) + : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) { + + // Register builtin checkers. #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ - addChecker(register##CLASS, FULLNAME, HELPTEXT, DOC_URI); +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ + addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \ + DOC_URI, IS_HIDDEN); + +#define GET_PACKAGES +#define PACKAGE(FULLNAME) addPackage(FULLNAME); + #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER #undef GET_CHECKERS +#undef PACKAGE +#undef GET_PACKAGES - for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end(); - i != e; ++i) { + // Register checkers from plugins. + for (const std::string &Plugin : Plugins) { // Get access to the plugin. - std::string err; - DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err); - if (!lib.isValid()) { - diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err; + std::string ErrorMsg; + DynamicLibrary Lib = + DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg); + if (!Lib.isValid()) { + Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg; continue; } - // See if it's compatible with this build of clang. - const char *pluginAPIVersion = - (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString"); - if (!isCompatibleAPIVersion(pluginAPIVersion)) { + // See if its compatible with this build of clang. + const char *PluginAPIVersion = static_cast<const char *>( + Lib.getAddressOfSymbol("clang_analyzerAPIVersionString")); + + if (!isCompatibleAPIVersion(PluginAPIVersion)) { Diags.Report(diag::warn_incompatible_analyzer_plugin_api) - << llvm::sys::path::filename(*i); + << llvm::sys::path::filename(Plugin); Diags.Report(diag::note_incompatible_analyzer_plugin_api) - << CLANG_ANALYZER_API_VERSION_STRING - << pluginAPIVersion; + << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion; continue; } // Register its checkers. - RegisterCheckersFn registerPluginCheckers = - (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol( - "clang_registerCheckers"); - if (registerPluginCheckers) - registerPluginCheckers(*this); + RegisterCheckersFn RegisterPluginCheckers = + reinterpret_cast<RegisterCheckersFn>( + Lib.getAddressOfSymbol("clang_registerCheckers")); + if (RegisterPluginCheckers) + RegisterPluginCheckers(*this); + } + + // Register statically linked checkers, that aren't generated from the tblgen + // file, but rather passed their registry function as a parameter in + // checkerRegistrationFns. + + for (const auto &Fn : CheckerRegistrationFns) + Fn(*this); + + // Sort checkers for efficient collection. + // FIXME: Alphabetical sort puts 'experimental' in the middle. + // Would it be better to name it '~experimental' or something else + // that's ASCIIbetically last? + llvm::sort(Packages, PackageNameLT{}); + llvm::sort(Checkers, CheckerNameLT{}); + +#define GET_CHECKER_DEPENDENCIES + +#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ + addDependency(FULLNAME, DEPENDENCY); + +#define GET_CHECKER_OPTIONS +#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \ + addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); + +#define GET_PACKAGE_OPTIONS +#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \ + addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); + +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER_DEPENDENCY +#undef GET_CHECKER_DEPENDENCIES +#undef CHECKER_OPTION +#undef GET_CHECKER_OPTIONS +#undef PACKAGE_OPTION +#undef GET_PACKAGE_OPTIONS + + resolveDependencies(); + resolveCheckerAndPackageOptions(); + + // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the + // command line. + for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersControlList) { + CheckerInfoListRange CheckerForCmdLineArg = + getMutableCheckersForCmdLineArg(Opt.first); + + if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) { + Diags.Report(diag::err_unknown_analyzer_checker) << Opt.first; + Diags.Report(diag::note_suggest_disabling_all_checkers); + } + + for (CheckerInfo &checker : CheckerForCmdLineArg) { + checker.State = Opt.second ? StateFromCmdLine::State_Enabled + : StateFromCmdLine::State_Disabled; + } } } -static constexpr char PackageSeparator = '.'; +/// Collects dependencies in \p ret, returns false on failure. +static bool +collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps, + const LangOptions &LO, + CheckerRegistry::CheckerInfoSet &Ret); + +/// Collects dependenies in \p enabledCheckers. Return None on failure. +LLVM_NODISCARD +static llvm::Optional<CheckerRegistry::CheckerInfoSet> +collectDependencies(const CheckerRegistry::CheckerInfo &checker, + const LangOptions &LO) { + + CheckerRegistry::CheckerInfoSet Ret; + // Add dependencies to the enabled checkers only if all of them can be + // enabled. + if (!collectDependenciesImpl(checker.Dependencies, LO, Ret)) + return None; + + return Ret; +} + +static bool +collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps, + const LangOptions &LO, + CheckerRegistry::CheckerInfoSet &Ret) { + + for (const CheckerRegistry::CheckerInfo *Dependency : Deps) { -static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, - const CheckerRegistry::CheckerInfo &b) { - return a.FullName < b.FullName; + if (Dependency->isDisabled(LO)) + return false; + + // Collect dependencies recursively. + if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret)) + return false; + + Ret.insert(Dependency); + } + + return true; } -static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, - StringRef packageName) { - // Does the checker's full name have the package as a prefix? - if (!checker.FullName.startswith(packageName)) - return false; +CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const { - // Is the package actually just the name of a specific checker? - if (checker.FullName.size() == packageName.size()) - return true; + CheckerInfoSet EnabledCheckers; - // Is the checker in the package (or a subpackage)? - if (checker.FullName[packageName.size()] == PackageSeparator) - return true; + for (const CheckerInfo &Checker : Checkers) { + if (!Checker.isEnabled(LangOpts)) + continue; - return false; + // Recursively enable its dependencies. + llvm::Optional<CheckerInfoSet> Deps = + collectDependencies(Checker, LangOpts); + + if (!Deps) { + // If we failed to enable any of the dependencies, don't enable this + // checker. + continue; + } + + // Note that set_union also preserves the order of insertion. + EnabledCheckers.set_union(*Deps); + + // Enable the checker. + EnabledCheckers.insert(&Checker); + } + + return EnabledCheckers; } -CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers( - const AnalyzerOptions &Opts) const { +void CheckerRegistry::resolveDependencies() { + for (const std::pair<StringRef, StringRef> &Entry : Dependencies) { + auto CheckerIt = binaryFind(Checkers, Entry.first); + assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first && + "Failed to find the checker while attempting to set up its " + "dependencies!"); - assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && - "In order to efficiently gather checkers, this function expects them " - "to be already sorted!"); + auto DependencyIt = binaryFind(Checkers, Entry.second); + assert(DependencyIt != Checkers.end() && + DependencyIt->FullName == Entry.second && + "Failed to find the dependency of a checker!"); - CheckerInfoSet enabledCheckers; - const auto end = Checkers.cend(); + CheckerIt->Dependencies.emplace_back(&*DependencyIt); + } - for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) { - // Use a binary search to find the possible start of the package. - CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "", ""); - auto firstRelatedChecker = - std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT); + Dependencies.clear(); +} - if (firstRelatedChecker == end || - !isInPackage(*firstRelatedChecker, opt.first)) { - Diags.Report(diag::err_unknown_analyzer_checker) << opt.first; - Diags.Report(diag::note_suggest_disabling_all_checkers); - return {}; +void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) { + Dependencies.emplace_back(FullName, Dependency); +} + +/// Insert the checker/package option to AnalyzerOptions' config table, and +/// validate it, if the user supplied it on the command line. +static void insertAndValidate(StringRef FullName, + const CheckerRegistry::CmdLineOption &Option, + AnalyzerOptions &AnOpts, + DiagnosticsEngine &Diags) { + + std::string FullOption = (FullName + ":" + Option.OptionName).str(); + + auto It = AnOpts.Config.insert({FullOption, Option.DefaultValStr}); + + // Insertation was successful -- CmdLineOption's constructor will validate + // whether values received from plugins or TableGen files are correct. + if (It.second) + return; + + // Insertion failed, the user supplied this package/checker option on the + // command line. If the supplied value is invalid, we'll restore the option + // to it's default value, and if we're in non-compatibility mode, we'll also + // emit an error. + + StringRef SuppliedValue = It.first->getValue(); + + if (Option.OptionType == "bool") { + if (SuppliedValue != "true" && SuppliedValue != "false") { + if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) { + Diags.Report(diag::err_analyzer_checker_option_invalid_input) + << FullOption << "a boolean value"; + } + + It.first->setValue(Option.DefaultValStr); } + return; + } - // See how large the package is. - // If the package doesn't exist, assume the option refers to a single - // checker. - size_t size = 1; - llvm::StringMap<size_t>::const_iterator packageSize = - Packages.find(opt.first); - if (packageSize != Packages.end()) - size = packageSize->getValue(); + if (Option.OptionType == "int") { + int Tmp; + bool HasFailed = SuppliedValue.getAsInteger(0, Tmp); + if (HasFailed) { + if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) { + Diags.Report(diag::err_analyzer_checker_option_invalid_input) + << FullOption << "an integer value"; + } - // Step through all the checkers in the package. - for (auto lastRelatedChecker = firstRelatedChecker+size; - firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) - if (opt.second) - enabledCheckers.insert(&*firstRelatedChecker); - else - enabledCheckers.remove(&*firstRelatedChecker); + It.first->setValue(Option.DefaultValStr); + } + return; } +} + +template <class T> +static void +insertOptionToCollection(StringRef FullName, T &Collection, + const CheckerRegistry::CmdLineOption &Option, + AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) { + auto It = binaryFind(Collection, FullName); + assert(It != Collection.end() && + "Failed to find the checker while attempting to add a command line " + "option to it!"); - return enabledCheckers; + insertAndValidate(FullName, Option, AnOpts, Diags); + + It->CmdLineOptions.emplace_back(Option); +} + +void CheckerRegistry::resolveCheckerAndPackageOptions() { + for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry : + CheckerOptions) { + insertOptionToCollection(CheckerOptEntry.first, Checkers, + CheckerOptEntry.second, AnOpts, Diags); + } + CheckerOptions.clear(); + + for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry : + PackageOptions) { + insertOptionToCollection(PackageOptEntry.first, Packages, + PackageOptEntry.second, AnOpts, Diags); + } + PackageOptions.clear(); +} + +void CheckerRegistry::addPackage(StringRef FullName) { + Packages.emplace_back(PackageInfo(FullName)); +} + +void CheckerRegistry::addPackageOption(StringRef OptionType, + StringRef PackageFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description, + StringRef DevelopmentStatus, + bool IsHidden) { + PackageOptions.emplace_back( + PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, + Description, DevelopmentStatus, IsHidden}); } -void CheckerRegistry::addChecker(InitializationFunction Fn, StringRef Name, - StringRef Desc, StringRef DocsUri) { - Checkers.emplace_back(Fn, Name, Desc, DocsUri); +void CheckerRegistry::addChecker(InitializationFunction Rfn, + ShouldRegisterFunction Sfn, StringRef Name, + StringRef Desc, StringRef DocsUri, + bool IsHidden) { + Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden); // Record the presence of the checker in its packages. - StringRef packageName, leafName; - std::tie(packageName, leafName) = Name.rsplit(PackageSeparator); - while (!leafName.empty()) { - Packages[packageName] += 1; - std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); + StringRef PackageName, LeafName; + std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator); + while (!LeafName.empty()) { + PackageSizes[PackageName] += 1; + std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator); } } -void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, - const AnalyzerOptions &Opts) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); +void CheckerRegistry::addCheckerOption(StringRef OptionType, + StringRef CheckerFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description, + StringRef DevelopmentStatus, + bool IsHidden) { + CheckerOptions.emplace_back( + CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, + Description, DevelopmentStatus, IsHidden}); +} +void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const { // Collect checkers enabled by the options. - CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts); + CheckerInfoSet enabledCheckers = getEnabledCheckers(); // Initialize the CheckerManager with all enabled checkers. - for (const auto *i : enabledCheckers) { - checkerMgr.setCurrentCheckName(CheckName(i->FullName)); - i->Initialize(checkerMgr); + for (const auto *Checker : enabledCheckers) { + CheckerMgr.setCurrentCheckName(CheckName(Checker->FullName)); + Checker->Initialize(CheckerMgr); + } +} + +static void +isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList, + StringRef SuppliedChecker, StringRef SuppliedOption, + const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) { + + if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue) + return; + + using CmdLineOption = CheckerRegistry::CmdLineOption; + + auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) { + return Opt.OptionName == SuppliedOption; + }; + + auto OptionIt = llvm::find_if(OptionList, SameOptName); + + if (OptionIt == OptionList.end()) { + Diags.Report(diag::err_analyzer_checker_option_unknown) + << SuppliedChecker << SuppliedOption; + return; } } -void CheckerRegistry::validateCheckerOptions( - const AnalyzerOptions &opts) const { - for (const auto &config : opts.Config) { - size_t pos = config.getKey().find(':'); - if (pos == StringRef::npos) +void CheckerRegistry::validateCheckerOptions() const { + for (const auto &Config : AnOpts.Config) { + + StringRef SuppliedChecker; + StringRef SuppliedOption; + std::tie(SuppliedChecker, SuppliedOption) = Config.getKey().split(':'); + + if (SuppliedOption.empty()) continue; - bool hasChecker = false; - StringRef checkerName = config.getKey().substr(0, pos); - for (const auto &checker : Checkers) { - if (checker.FullName.startswith(checkerName) && - (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { - hasChecker = true; - break; - } + // AnalyzerOptions' config table contains the user input, so an entry could + // look like this: + // + // cor:NoFalsePositives=true + // + // Since lower_bound would look for the first element *not less* than "cor", + // it would return with an iterator to the first checker in the core, so we + // we really have to use find here, which uses operator==. + auto CheckerIt = llvm::find(Checkers, CheckerInfo(SuppliedChecker)); + if (CheckerIt != Checkers.end()) { + isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedChecker, + SuppliedOption, AnOpts, Diags); + continue; } - if (!hasChecker) - Diags.Report(diag::err_unknown_analyzer_checker) << checkerName; + + auto PackageIt = llvm::find(Packages, PackageInfo(SuppliedChecker)); + if (PackageIt != Packages.end()) { + isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedChecker, + SuppliedOption, AnOpts, Diags); + continue; + } + + Diags.Report(diag::err_unknown_analyzer_checker) << SuppliedChecker; } } -void CheckerRegistry::printHelp(raw_ostream &out, - size_t maxNameChars) const { - // FIXME: Alphabetical sort puts 'experimental' in the middle. - // Would it be better to name it '~experimental' or something else - // that's ASCIIbetically last? - llvm::sort(Checkers, checkerNameLT); - +void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out, + size_t MaxNameChars) const { // FIXME: Print available packages. - out << "CHECKERS:\n"; + Out << "CHECKERS:\n"; // Find the maximum option length. - size_t optionFieldWidth = 0; - for (const auto &i : Checkers) { + size_t OptionFieldWidth = 0; + for (const auto &Checker : Checkers) { // Limit the amount of padding we are willing to give up for alignment. // Package.Name Description [Hidden] - size_t nameLength = i.FullName.size(); - if (nameLength <= maxNameChars) - optionFieldWidth = std::max(optionFieldWidth, nameLength); + size_t NameLength = Checker.FullName.size(); + if (NameLength <= MaxNameChars) + OptionFieldWidth = std::max(OptionFieldWidth, NameLength); } - const size_t initialPad = 2; - for (const auto &i : Checkers) { - out.indent(initialPad) << i.FullName; - - int pad = optionFieldWidth - i.FullName.size(); + const size_t InitialPad = 2; + + auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker, + StringRef Description) { + AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description}, + InitialPad, OptionFieldWidth); + Out << '\n'; + }; + + for (const auto &Checker : Checkers) { + // The order of this if branches is significant, we wouldn't like to display + // developer checkers even in the alpha output. For example, + // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden + // by default, and users (even when the user is a developer of an alpha + // checker) shouldn't normally tinker with whether they should be enabled. + + if (Checker.IsHidden) { + if (AnOpts.ShowCheckerHelpDeveloper) + Print(Out, Checker, Checker.Desc); + continue; + } - // Break on long option names. - if (pad < 0) { - out << '\n'; - pad = optionFieldWidth + initialPad; + if (Checker.FullName.startswith("alpha")) { + if (AnOpts.ShowCheckerHelpAlpha) + Print(Out, Checker, + ("(Enable only for development!) " + Checker.Desc).str()); + continue; } - out.indent(pad + 2) << i.Desc; - out << '\n'; + if (AnOpts.ShowCheckerHelp) + Print(Out, Checker, Checker.Desc); } } -void CheckerRegistry::printList(raw_ostream &out, - const AnalyzerOptions &opts) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); - +void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const { // Collect checkers enabled by the options. - CheckerInfoSet enabledCheckers = getEnabledCheckers(opts); + CheckerInfoSet EnabledCheckers = getEnabledCheckers(); + + for (const auto *i : EnabledCheckers) + Out << i->FullName << '\n'; +} + +void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const { + Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n"; + Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; + Out << " -analyzer-config OPTION1=VALUE, -analyzer-config " + "OPTION2=VALUE, ...\n\n"; + Out << "OPTIONS:\n\n"; + + std::multimap<StringRef, const CmdLineOption &> OptionMap; + + for (const CheckerInfo &Checker : Checkers) { + for (const CmdLineOption &Option : Checker.CmdLineOptions) { + OptionMap.insert({Checker.FullName, Option}); + } + } + + for (const PackageInfo &Package : Packages) { + for (const CmdLineOption &Option : Package.CmdLineOptions) { + OptionMap.insert({Package.FullName, Option}); + } + } + + auto Print = [] (llvm::raw_ostream &Out, StringRef FullOption, StringRef Desc) { + AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc}, + /*InitialPad*/ 2, + /*EntryWidth*/ 50, + /*MinLineWidth*/ 90); + Out << "\n\n"; + }; + for (const std::pair<StringRef, const CmdLineOption &> &Entry : OptionMap) { + const CmdLineOption &Option = Entry.second; + std::string FullOption = (Entry.first + ":" + Option.OptionName).str(); + + std::string Desc = + ("(" + Option.OptionType + ") " + Option.Description + " (default: " + + (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")") + .str(); + + // The list of these if branches is significant, we wouldn't like to + // display hidden alpha checker options for + // -analyzer-checker-option-help-alpha. + + if (Option.IsHidden) { + if (AnOpts.ShowCheckerOptionDeveloperList) + Print(Out, FullOption, Desc); + continue; + } - for (const auto *i : enabledCheckers) - out << i->FullName << '\n'; + if (Option.DevelopmentStatus == "alpha" || + Entry.first.startswith("alpha")) { + if (AnOpts.ShowCheckerOptionAlphaList) + Print(Out, FullOption, + llvm::Twine("(Enable only for development!) " + Desc).str()); + continue; + } + + if (AnOpts.ShowCheckerOptionList) + Print(Out, FullOption, Desc); + } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp index b33608042ce3..a8af6b3d801a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -1,9 +1,8 @@ //===--- FrontendActions.cpp ----------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp index 60825ef7411d..276f7313b08f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp @@ -1,9 +1,8 @@ //===--- ModelConsumer.cpp - ASTConsumer for consuming model files --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp index b1927c8401d6..fe5f59045cde 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -1,9 +1,8 @@ //===-- ModelInjector.cpp ---------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -83,8 +82,6 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) { Instance.getDiagnostics().setSourceManager(&SM); - Instance.setVirtualFileSystem(&CI.getVirtualFileSystem()); - // The instance wants to take ownership, however DisableFree frontend option // is set to true to avoid double free issues Instance.setFileManager(&CI.getFileManager()); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h index b1b6de9ef9d9..d2016c3b112c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h @@ -1,9 +1,8 @@ //===-- ModelInjector.h -----------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// |