Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@ -0,0 +1,3 @@
add_subdirectory(Core)
add_subdirectory(Checkers)
add_subdirectory(Frontend)

View File

@ -0,0 +1,24 @@
//=- AllocationDiagnostics.cpp - Config options for allocation diags *- C++ -*-//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Declares the configuration functions for leaks/allocation diagnostics.
//
//===--------------------------
#include "AllocationDiagnostics.h"
namespace clang {
namespace ento {
bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts) {
return AOpts.getBooleanOption("leak-diagnostics-reference-allocation",
false);
}
}}

View File

@ -0,0 +1,31 @@
//=--- AllocationDiagnostics.h - Config options for allocation diags *- C++ -*-//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Declares the configuration functions for leaks/allocation diagnostics.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONDIAGNOSTICS_H
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONDIAGNOSTICS_H
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
namespace clang { namespace ento {
/// \brief Returns true if leak diagnostics should directly reference
/// the allocatin site (where possible).
///
/// The default is false.
///
bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts);
}}
#endif

View File

@ -0,0 +1,99 @@
//===- 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.
//
//===----------------------------------------------------------------------===//
//
// This checker prints callbacks that are called during analysis.
// This is required to ensure that callbacks are fired in order
// and do not duplicate or get lost.
// Feel free to extend this checker with any callback you need to check.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class AnalysisOrderChecker
: public Checker<check::PreStmt<CastExpr>,
check::PostStmt<CastExpr>,
check::PreStmt<ArraySubscriptExpr>,
check::PostStmt<ArraySubscriptExpr>,
check::Bind,
check::RegionChanges> {
bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
return Opts.getBooleanOption("*", false, this) ||
Opts.getBooleanOption(CallbackName, false, this);
}
bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const {
AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions();
return isCallbackEnabled(Opts, CallbackName);
}
bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const {
AnalyzerOptions &Opts = State->getStateManager().getOwningEngine()
->getAnalysisManager().getAnalyzerOptions();
return isCallbackEnabled(Opts, CallbackName);
}
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const {
if (isCallbackEnabled(C, "PreStmtCastExpr"))
llvm::errs() << "PreStmt<CastExpr> (Kind : " << CE->getCastKindName()
<< ")\n";
}
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const {
if (isCallbackEnabled(C, "PostStmtCastExpr"))
llvm::errs() << "PostStmt<CastExpr> (Kind : " << CE->getCastKindName()
<< ")\n";
}
void checkPreStmt(const ArraySubscriptExpr *SubExpr,
CheckerContext &C) const {
if (isCallbackEnabled(C, "PreStmtArraySubscriptExpr"))
llvm::errs() << "PreStmt<ArraySubscriptExpr>\n";
}
void checkPostStmt(const ArraySubscriptExpr *SubExpr,
CheckerContext &C) const {
if (isCallbackEnabled(C, "PostStmtArraySubscriptExpr"))
llvm::errs() << "PostStmt<ArraySubscriptExpr>\n";
}
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
if (isCallbackEnabled(C, "Bind"))
llvm::errs() << "Bind\n";
}
ProgramStateRef
checkRegionChanges(ProgramStateRef State,
const InvalidatedSymbols *Invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx, const CallEvent *Call) const {
if (isCallbackEnabled(State, "RegionChanges"))
llvm::errs() << "RegionChanges\n";
return State;
}
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Registration.
//===----------------------------------------------------------------------===//
void ento::registerAnalysisOrderChecker(CheckerManager &mgr) {
mgr.registerChecker<AnalysisOrderChecker>();
}

View File

@ -0,0 +1,140 @@
//==--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.
//
//===----------------------------------------------------------------------===//
// This file reports various statistics about analyzer visitation.
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
#define DEBUG_TYPE "StatsChecker"
STATISTIC(NumBlocks,
"The # of blocks in top level functions");
STATISTIC(NumBlocksUnreachable,
"The # of unreachable blocks in analyzing top level functions");
namespace {
class AnalyzerStatsChecker : public Checker<check::EndAnalysis> {
public:
void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const;
};
}
void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
BugReporter &B,
ExprEngine &Eng) const {
const CFG *C = nullptr;
const SourceManager &SM = B.getSourceManager();
llvm::SmallPtrSet<const CFGBlock*, 32> reachable;
// Root node should have the location context of the top most function.
const ExplodedNode *GraphRoot = *G.roots_begin();
const LocationContext *LC = GraphRoot->getLocation().getLocationContext();
const Decl *D = LC->getDecl();
// Iterate over the exploded graph.
for (ExplodedGraph::node_iterator I = G.nodes_begin();
I != G.nodes_end(); ++I) {
const ProgramPoint &P = I->getLocation();
// Only check the coverage in the top level function (optimization).
if (D != P.getLocationContext()->getDecl())
continue;
if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
const CFGBlock *CB = BE->getBlock();
reachable.insert(CB);
}
}
// Get the CFG and the Decl of this block.
C = LC->getCFG();
unsigned total = 0, unreachable = 0;
// Find CFGBlocks that were not covered by any node
for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
const CFGBlock *CB = *I;
++total;
// Check if the block is unreachable
if (!reachable.count(CB)) {
++unreachable;
}
}
// We never 'reach' the entry block, so correct the unreachable count
unreachable--;
// There is no BlockEntrance corresponding to the exit block as well, so
// assume it is reached as well.
unreachable--;
// Generate the warning string
SmallString<128> buf;
llvm::raw_svector_ostream output(buf);
PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
if (!Loc.isValid())
return;
if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
const NamedDecl *ND = cast<NamedDecl>(D);
output << *ND;
}
else if (isa<BlockDecl>(D)) {
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
}
NumBlocksUnreachable += unreachable;
NumBlocks += total;
std::string NameOfRootFunction = output.str();
output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "
<< unreachable << " | Exhausted Block: "
<< (Eng.wasBlocksExhausted() ? "yes" : "no")
<< " | Empty WorkList: "
<< (Eng.hasEmptyWorkList() ? "yes" : "no");
B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",
output.str(), PathDiagnosticLocation(D, SM));
// Emit warning for each block we bailed out on.
typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
const CoreEngine &CE = Eng.getCoreEngine();
for (ExhaustedIterator I = CE.blocks_exhausted_begin(),
E = CE.blocks_exhausted_end(); I != E; ++I) {
const BlockEdge &BE = I->first;
const CFGBlock *Exit = BE.getDst();
const CFGElement &CE = Exit->front();
if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
SmallString<128> bufI;
llvm::raw_svector_ostream outputI(bufI);
outputI << "(" << NameOfRootFunction << ")" <<
": The analyzer generated a sink at this point";
B.EmitBasicReport(
D, this, "Sink Point", "Internal Statistics", outputI.str(),
PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
}
}
}
void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {
mgr.registerChecker<AnalyzerStatsChecker>();
}

View File

@ -0,0 +1,93 @@
//== ArrayBoundChecker.cpp ------------------------------*- 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 ArrayBoundChecker, which is a path-sensitive check
// which looks for an out-of-bound array element access.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.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/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
using namespace clang;
using namespace ento;
namespace {
class ArrayBoundChecker :
public Checker<check::Location> {
mutable std::unique_ptr<BuiltinBug> BT;
public:
void checkLocation(SVal l, bool isLoad, const Stmt* S,
CheckerContext &C) const;
};
}
void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
CheckerContext &C) const {
// Check for out of bound array element access.
const MemRegion *R = l.getAsRegion();
if (!R)
return;
const ElementRegion *ER = dyn_cast<ElementRegion>(R);
if (!ER)
return;
// Get the index of the accessed element.
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
// Zero index is always in bound, this also passes ElementRegions created for
// pointer casts.
if (Idx.isZeroConstant())
return;
ProgramStateRef state = C.getState();
// Get the size of the array.
DefinedOrUnknownSVal NumElements
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
ER->getValueType());
ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
if (!N)
return;
if (!BT)
BT.reset(new BuiltinBug(
this, "Out-of-bound array access",
"Access out-of-bound array element (buffer overflow)"));
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
// Generate a report for this bug.
auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
report->addRange(LoadS->getSourceRange());
C.emitReport(std::move(report));
return;
}
// Array bound check succeeded. From this point forward the array bound
// should always succeed.
C.addTransition(StInBound);
}
void ento::registerArrayBoundChecker(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundChecker>();
}

View File

@ -0,0 +1,355 @@
//== ArrayBoundCheckerV2.cpp ------------------------------------*- 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 ArrayBoundCheckerV2, which is a path-sensitive check
// which looks for an out-of-bound array element access.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/CharUnits.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/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
class ArrayBoundCheckerV2 :
public Checker<check::Location> {
mutable std::unique_ptr<BuiltinBug> BT;
enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
void reportOOB(CheckerContext &C, ProgramStateRef errorState,
OOB_Kind kind) const;
public:
void checkLocation(SVal l, bool isLoad, const Stmt*S,
CheckerContext &C) const;
};
// FIXME: Eventually replace RegionRawOffset with this class.
class RegionRawOffsetV2 {
private:
const SubRegion *baseRegion;
SVal byteOffset;
RegionRawOffsetV2()
: baseRegion(nullptr), byteOffset(UnknownVal()) {}
public:
RegionRawOffsetV2(const SubRegion* base, SVal offset)
: baseRegion(base), byteOffset(offset) {}
NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
const SubRegion *getRegion() const { return baseRegion; }
static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
SValBuilder &svalBuilder,
SVal location);
void dump() const;
void dumpToStream(raw_ostream &os) const;
};
}
static SVal computeExtentBegin(SValBuilder &svalBuilder,
const MemRegion *region) {
const MemSpaceRegion *SR = region->getMemorySpace();
if (SR->getKind() == MemRegion::UnknownSpaceRegionKind)
return UnknownVal();
else
return svalBuilder.makeZeroArrayIndex();
}
// TODO: once the constraint manager is smart enough to handle non simplified
// symbolic expressions remove this function. Note that this can not be used in
// the constraint manager as is, since this does not handle overflows. It is
// safe to assume, however, that memory offsets will not overflow.
static std::pair<NonLoc, nonloc::ConcreteInt>
getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
SValBuilder &svalBuilder) {
Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) {
llvm::APSInt constant =
APSIntType(extent.getValue()).convert(SIE->getRHS());
switch (SIE->getOpcode()) {
case BO_Mul:
// The constant should never be 0 here, since it the result of scaling
// based on the size of a type which is never 0.
if ((extent.getValue() % constant) != 0)
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
else
return getSimplifiedOffsets(
nonloc::SymbolVal(SIE->getLHS()),
svalBuilder.makeIntVal(extent.getValue() / constant),
svalBuilder);
case BO_Add:
return getSimplifiedOffsets(
nonloc::SymbolVal(SIE->getLHS()),
svalBuilder.makeIntVal(extent.getValue() - constant), svalBuilder);
default:
break;
}
}
}
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
}
void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
const Stmt* LoadS,
CheckerContext &checkerContext) const {
// NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
// some new logic here that reasons directly about memory region extents.
// Once that logic is more mature, we can bring it back to assumeInBound()
// for all clients to use.
//
// The algorithm we are using here for bounds checking is to see if the
// memory access is within the extent of the base region. Since we
// have some flexibility in defining the base region, we can achieve
// various levels of conservatism in our buffer overflow checking.
ProgramStateRef state = checkerContext.getState();
ProgramStateRef originalState = state;
SValBuilder &svalBuilder = checkerContext.getSValBuilder();
const RegionRawOffsetV2 &rawOffset =
RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
if (!rawOffset.getRegion())
return;
NonLoc rawOffsetVal = rawOffset.getByteOffset();
// CHECK LOWER BOUND: Is byteOffset < extent begin?
// If so, we are doing a load/store
// before the first valid offset in the memory region.
SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
if (NV->getAs<nonloc::ConcreteInt>()) {
std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
getSimplifiedOffsets(rawOffset.getByteOffset(),
NV->castAs<nonloc::ConcreteInt>(),
svalBuilder);
rawOffsetVal = simplifiedOffsets.first;
*NV = simplifiedOffsets.second;
}
SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV,
svalBuilder.getConditionType());
Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
if (!lowerBoundToCheck)
return;
ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
std::tie(state_precedesLowerBound, state_withinLowerBound) =
state->assume(*lowerBoundToCheck);
// Are we constrained enough to definitely precede the lower bound?
if (state_precedesLowerBound && !state_withinLowerBound) {
reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
return;
}
// Otherwise, assume the constraint of the lower bound.
assert(state_withinLowerBound);
state = state_withinLowerBound;
}
do {
// CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
// we are doing a load/store after the last valid offset.
DefinedOrUnknownSVal extentVal =
rawOffset.getRegion()->getExtent(svalBuilder);
if (!extentVal.getAs<NonLoc>())
break;
if (extentVal.getAs<nonloc::ConcreteInt>()) {
std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
getSimplifiedOffsets(rawOffset.getByteOffset(),
extentVal.castAs<nonloc::ConcreteInt>(),
svalBuilder);
rawOffsetVal = simplifiedOffsets.first;
extentVal = simplifiedOffsets.second;
}
SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal,
extentVal.castAs<NonLoc>(),
svalBuilder.getConditionType());
Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
if (!upperboundToCheck)
break;
ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
std::tie(state_exceedsUpperBound, state_withinUpperBound) =
state->assume(*upperboundToCheck);
// If we are under constrained and the index variables are tainted, report.
if (state_exceedsUpperBound && state_withinUpperBound) {
if (state->isTainted(rawOffset.getByteOffset())) {
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
return;
}
} else if (state_exceedsUpperBound) {
// If we are constrained enough to definitely exceed the upper bound,
// report.
assert(!state_withinUpperBound);
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
return;
}
assert(state_withinUpperBound);
state = state_withinUpperBound;
}
while (false);
if (state != originalState)
checkerContext.addTransition(state);
}
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
ProgramStateRef errorState,
OOB_Kind kind) const {
ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState);
if (!errorNode)
return;
if (!BT)
BT.reset(new BuiltinBug(this, "Out-of-bound access"));
// FIXME: This diagnostics are preliminary. We should get far better
// diagnostics for explaining buffer overruns.
SmallString<256> buf;
llvm::raw_svector_ostream os(buf);
os << "Out of bound memory access ";
switch (kind) {
case OOB_Precedes:
os << "(accessed memory precedes memory block)";
break;
case OOB_Excedes:
os << "(access exceeds upper limit of memory block)";
break;
case OOB_Tainted:
os << "(index is tainted)";
break;
}
checkerContext.emitReport(
llvm::make_unique<BugReport>(*BT, os.str(), errorNode));
}
#ifndef NDEBUG
LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const {
dumpToStream(llvm::errs());
}
void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
}
#endif
// Lazily computes a value to be used by 'computeOffset'. If 'val'
// is unknown or undefined, we lazily substitute '0'. Otherwise,
// return 'val'.
static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val;
}
// Scale a base value by a scaling factor, and return the scaled
// value as an SVal. Used by 'computeOffset'.
static inline SVal scaleValue(ProgramStateRef state,
NonLoc baseVal, CharUnits scaling,
SValBuilder &sb) {
return sb.evalBinOpNN(state, BO_Mul, baseVal,
sb.makeArrayIndex(scaling.getQuantity()),
sb.getArrayIndexType());
}
// Add an SVal to another, treating unknown and undefined values as
// summing to UnknownVal. Used by 'computeOffset'.
static SVal addValue(ProgramStateRef state, SVal x, SVal y,
SValBuilder &svalBuilder) {
// We treat UnknownVals and UndefinedVals the same here because we
// only care about computing offsets.
if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
return UnknownVal();
return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(),
y.castAs<NonLoc>(),
svalBuilder.getArrayIndexType());
}
/// Compute a raw byte offset from a base region. Used for array bounds
/// checking.
RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
SValBuilder &svalBuilder,
SVal location)
{
const MemRegion *region = location.getAsRegion();
SVal offset = UndefinedVal();
while (region) {
switch (region->getKind()) {
default: {
if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
offset = getValue(offset, svalBuilder);
if (!offset.isUnknownOrUndef())
return RegionRawOffsetV2(subReg, offset);
}
return RegionRawOffsetV2();
}
case MemRegion::ElementRegionKind: {
const ElementRegion *elemReg = cast<ElementRegion>(region);
SVal index = elemReg->getIndex();
if (!index.getAs<NonLoc>())
return RegionRawOffsetV2();
QualType elemType = elemReg->getElementType();
// If the element is an incomplete type, go no further.
ASTContext &astContext = svalBuilder.getContext();
if (elemType->isIncompleteType())
return RegionRawOffsetV2();
// Update the offset.
offset = addValue(state,
getValue(offset, svalBuilder),
scaleValue(state,
index.castAs<NonLoc>(),
astContext.getTypeSizeInChars(elemType),
svalBuilder),
svalBuilder);
if (offset.isUnknownOrUndef())
return RegionRawOffsetV2();
region = elemReg->getSuperRegion();
continue;
}
}
}
return RegionRawOffsetV2();
}
void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundCheckerV2>();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,185 @@
//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines a checker for blocks in critical sections. This checker should find
// the calls to blocking functions (for example: sleep, getc, fgets, read,
// recv etc.) inside a critical section. When sleep(x) is called while a mutex
// is held, other threades cannot lock the same mutex. This might take some
// time, leading to bad performance or even deadlock.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
StringRef ClassLockGuard, ClassUniqueLock;
mutable bool IdentifierInfoInitialized;
std::unique_ptr<BugType> BlockInCritSectionBugType;
void initIdentifierInfo(ASTContext &Ctx) const;
void reportBlockInCritSection(SymbolRef FileDescSym,
const CallEvent &call,
CheckerContext &C) const;
public:
BlockInCriticalSectionChecker();
bool isBlockingFunction(const CallEvent &Call) const;
bool isLockFunction(const CallEvent &Call) const;
bool isUnlockFunction(const CallEvent &Call) const;
/// Process unlock.
/// Process lock.
/// Process blocking functions (sleep, getc, fgets, read, recv)
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
};
} // end anonymous namespace
REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
: IILockGuard(nullptr), IIUniqueLock(nullptr),
LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
PthreadLockFn("pthread_mutex_lock"),
PthreadTryLockFn("pthread_mutex_trylock"),
PthreadUnlockFn("pthread_mutex_unlock"),
MtxLock("mtx_lock"),
MtxTimedLock("mtx_timedlock"),
MtxTryLock("mtx_trylock"),
MtxUnlock("mtx_unlock"),
ClassLockGuard("lock_guard"),
ClassUniqueLock("unique_lock"),
IdentifierInfoInitialized(false) {
// Initialize the bug type.
BlockInCritSectionBugType.reset(
new BugType(this, "Call to blocking function in critical section",
"Blocking Error"));
}
void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (!IdentifierInfoInitialized) {
/* In case of checking C code, or when the corresponding headers are not
* included, we might end up query the identifier table every time when this
* function is called instead of early returning it. To avoid this, a bool
* variable (IdentifierInfoInitialized) is used and the function will be run
* only once. */
IILockGuard = &Ctx.Idents.get(ClassLockGuard);
IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
IdentifierInfoInitialized = true;
}
}
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
if (Call.isCalled(SleepFn)
|| Call.isCalled(GetcFn)
|| Call.isCalled(FgetsFn)
|| Call.isCalled(ReadFn)
|| Call.isCalled(RecvFn)) {
return true;
}
return false;
}
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
return true;
}
if (Call.isCalled(LockFn)
|| Call.isCalled(PthreadLockFn)
|| Call.isCalled(PthreadTryLockFn)
|| Call.isCalled(MtxLock)
|| Call.isCalled(MtxTimedLock)
|| Call.isCalled(MtxTryLock)) {
return true;
}
return false;
}
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
auto IdentifierInfo = DRecordDecl->getIdentifier();
if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
return true;
}
if (Call.isCalled(UnlockFn)
|| Call.isCalled(PthreadUnlockFn)
|| Call.isCalled(MtxUnlock)) {
return true;
}
return false;
}
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
initIdentifierInfo(C.getASTContext());
if (!isBlockingFunction(Call)
&& !isLockFunction(Call)
&& !isUnlockFunction(Call))
return;
ProgramStateRef State = C.getState();
unsigned mutexCount = State->get<MutexCounter>();
if (isUnlockFunction(Call) && mutexCount > 0) {
State = State->set<MutexCounter>(--mutexCount);
C.addTransition(State);
} else if (isLockFunction(Call)) {
State = State->set<MutexCounter>(++mutexCount);
C.addTransition(State);
} else if (mutexCount > 0) {
SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
reportBlockInCritSection(BlockDesc, Call, C);
}
}
void BlockInCriticalSectionChecker::reportBlockInCritSection(
SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
if (!ErrNode)
return;
std::string msg;
llvm::raw_string_ostream os(msg);
os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
<< "' inside of critical section";
auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
R->addRange(Call.getSourceRange());
R->markInteresting(BlockDescSym);
C.emitReport(std::move(R));
}
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
mgr.registerChecker<BlockInCriticalSectionChecker>();
}

View File

@ -0,0 +1,157 @@
//== 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.
//
//===----------------------------------------------------------------------===//
//
// This defines BoolAssignmentChecker, a builtin check in ExprEngine that
// performs checks for assignment of non-Boolean values to Boolean variables.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.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/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class BoolAssignmentChecker : public Checker< check::Bind > {
mutable std::unique_ptr<BuiltinBug> BT;
void emitReport(ProgramStateRef state, CheckerContext &C) const;
public:
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
};
} // end anonymous namespace
void BoolAssignmentChecker::emitReport(ProgramStateRef state,
CheckerContext &C) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
if (!BT)
BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
C.emitReport(llvm::make_unique<BugReport>(*BT, BT->getDescription(), N));
}
}
static bool isBooleanType(QualType Ty) {
if (Ty->isBooleanType()) // C++ or C99
return true;
if (const TypedefType *TT = Ty->getAs<TypedefType>())
return TT->getDecl()->getName() == "BOOL" || // Objective-C
TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99
TT->getDecl()->getName() == "Boolean"; // MacTypes.h
return false;
}
void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
CheckerContext &C) const {
// We are only interested in stores into Booleans.
const TypedValueRegion *TR =
dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion());
if (!TR)
return;
QualType valTy = TR->getValueType();
if (!isBooleanType(valTy))
return;
// Get the value of the right-hand side. We only care about values
// that are defined (UnknownVals and UndefinedVals are handled by other
// checkers).
Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
if (!DV)
return;
// Check if the assigned value meets our criteria for correctness. It must
// be a value that is either 0 or 1. One way to check this is to see if
// the value is possibly < 0 (for a negative value) or greater than 1.
ProgramStateRef state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
ConstraintManager &CM = C.getConstraintManager();
// First, ensure that the value is >= 0.
DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
SVal greaterThanOrEqualToZeroVal =
svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
svalBuilder.getConditionType());
Optional<DefinedSVal> greaterThanEqualToZero =
greaterThanOrEqualToZeroVal.getAs<DefinedSVal>();
if (!greaterThanEqualToZero) {
// The SValBuilder cannot construct a valid SVal for this condition.
// This means we cannot properly reason about it.
return;
}
ProgramStateRef stateLT, stateGE;
std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
// Is it possible for the value to be less than zero?
if (stateLT) {
// It is possible for the value to be less than zero. We only
// want to emit a warning, however, if that value is fully constrained.
// If it it possible for the value to be >= 0, then essentially the
// value is underconstrained and there is nothing left to be done.
if (!stateGE)
emitReport(stateLT, C);
// In either case, we are done.
return;
}
// If we reach here, it must be the case that the value is constrained
// to only be >= 0.
assert(stateGE == state);
// At this point we know that the value is >= 0.
// Now check to ensure that the value is <= 1.
DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
SVal lessThanEqToOneVal =
svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal,
svalBuilder.getConditionType());
Optional<DefinedSVal> lessThanEqToOne =
lessThanEqToOneVal.getAs<DefinedSVal>();
if (!lessThanEqToOne) {
// The SValBuilder cannot construct a valid SVal for this condition.
// This means we cannot properly reason about it.
return;
}
ProgramStateRef stateGT, stateLE;
std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
// Is it possible for the value to be greater than one?
if (stateGT) {
// It is possible for the value to be greater than one. We only
// want to emit a warning, however, if that value is fully constrained.
// If it is possible for the value to be <= 1, then essentially the
// value is underconstrained and there is nothing left to be done.
if (!stateLE)
emitReport(stateGT, C);
// In either case, we are done.
return;
}
// If we reach here, it must be the case that the value is constrained
// to only be <= 1.
assert(stateLE == state);
}
void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
mgr.registerChecker<BoolAssignmentChecker>();
}

View File

@ -0,0 +1,121 @@
//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This checker evaluates clang builtin functions.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/Basic/Builtins.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class BuiltinFunctionChecker : public Checker<eval::Call> {
public:
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
};
}
bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
const FunctionDecl *FD = C.getCalleeDecl(CE);
const LocationContext *LCtx = C.getLocationContext();
if (!FD)
return false;
switch (FD->getBuiltinID()) {
default:
return false;
case Builtin::BI__builtin_assume: {
assert (CE->arg_begin() != CE->arg_end());
SVal ArgSVal = state->getSVal(CE->getArg(0), LCtx);
if (ArgSVal.isUndef())
return true; // Return true to model purity.
state = state->assume(ArgSVal.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) {
C.generateSink(C.getState(), C.getPredecessor());
return true;
}
C.addTransition(state);
return true;
}
case Builtin::BI__builtin_unpredictable:
case Builtin::BI__builtin_expect:
case Builtin::BI__builtin_assume_aligned:
case Builtin::BI__builtin_addressof: {
// For __builtin_unpredictable, __builtin_expect, and
// __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 = state->getSVal(*(CE->arg_begin()), LCtx);
C.addTransition(state->BindExpr(CE, LCtx, X));
return true;
}
case Builtin::BI__builtin_alloca_with_align:
case Builtin::BI__builtin_alloca: {
// FIXME: Refactor into StoreManager itself?
MemRegionManager& RM = C.getStoreManager().getRegionManager();
const AllocaRegion* R =
RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext());
// 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.
DefinedOrUnknownSVal Size =
state->getSVal(*(CE->arg_begin()), LCtx).castAs<DefinedOrUnknownSVal>();
SValBuilder& svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
DefinedOrUnknownSVal extentMatchesSizeArg =
svalBuilder.evalEQ(state, Extent, Size);
state = state->assume(extentMatchesSizeArg, true);
assert(state && "The region should not have any previous constraints");
C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
return true;
}
case Builtin::BI__builtin_object_size: {
// This must be resolvable at compile time, so we defer to the constant
// evaluator for a value.
SVal V = UnknownVal();
llvm::APSInt Result;
if (CE->EvaluateAsInt(Result, C.getASTContext(), Expr::SE_NoSideEffects)) {
// Make sure the result has the correct type.
SValBuilder &SVB = C.getSValBuilder();
BasicValueFactory &BVF = SVB.getBasicValueFactory();
BVF.getAPSIntType(CE->getType()).apply(Result);
V = SVB.makeIntVal(Result);
}
C.addTransition(state->BindExpr(CE, LCtx, V));
return true;
}
}
}
void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<BuiltinFunctionChecker>();
}

View File

@ -0,0 +1,107 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangStaticAnalyzerCheckers
AllocationDiagnostics.cpp
AnalysisOrderChecker.cpp
AnalyzerStatsChecker.cpp
ArrayBoundChecker.cpp
ArrayBoundCheckerV2.cpp
BasicObjCFoundationChecks.cpp
BlockInCriticalSectionChecker.cpp
BoolAssignmentChecker.cpp
BuiltinFunctionChecker.cpp
CStringChecker.cpp
CStringSyntaxChecker.cpp
CallAndMessageChecker.cpp
CastSizeChecker.cpp
CastToStructChecker.cpp
CheckObjCDealloc.cpp
CheckObjCInstMethSignature.cpp
CheckSecuritySyntaxOnly.cpp
CheckSizeofPointer.cpp
CheckerDocumentation.cpp
ChrootChecker.cpp
ClangCheckers.cpp
CloneChecker.cpp
ConversionChecker.cpp
CXXSelfAssignmentChecker.cpp
DeadStoresChecker.cpp
DebugCheckers.cpp
DeleteWithNonVirtualDtorChecker.cpp
DereferenceChecker.cpp
DirectIvarAssignment.cpp
DivZeroChecker.cpp
DynamicTypePropagation.cpp
DynamicTypeChecker.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
GenericTaintChecker.cpp
GTestChecker.cpp
IdenticalExprChecker.cpp
IteratorChecker.cpp
IvarInvalidationChecker.cpp
LLVMConventionsChecker.cpp
LocalizationChecker.cpp
MacOSKeychainAPIChecker.cpp
MacOSXAPIChecker.cpp
MallocChecker.cpp
MallocOverflowSecurityChecker.cpp
MallocSizeofChecker.cpp
MisusedMovedObjectChecker.cpp
MPI-Checker/MPIBugReporter.cpp
MPI-Checker/MPIChecker.cpp
MPI-Checker/MPIFunctionClassifier.cpp
NSAutoreleasePoolChecker.cpp
NSErrorChecker.cpp
NoReturnFunctionChecker.cpp
NonNullParamChecker.cpp
NonnullGlobalConstantsChecker.cpp
NullabilityChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
ObjCContainersASTChecker.cpp
ObjCContainersChecker.cpp
ObjCMissingSuperCallChecker.cpp
ObjCPropertyChecker.cpp
ObjCSelfInitChecker.cpp
ObjCSuperDeallocChecker.cpp
ObjCUnusedIVarsChecker.cpp
PaddingChecker.cpp
PointerArithChecker.cpp
PointerSubChecker.cpp
PthreadLockChecker.cpp
RetainCountChecker.cpp
ReturnPointerRangeChecker.cpp
ReturnUndefChecker.cpp
SimpleStreamChecker.cpp
StackAddrEscapeChecker.cpp
StdLibraryFunctionsChecker.cpp
StreamChecker.cpp
TaintTesterChecker.cpp
TestAfterDivZeroChecker.cpp
TraversalChecker.cpp
UndefBranchChecker.cpp
UndefCapturedBlockVarChecker.cpp
UndefResultChecker.cpp
UndefinedArraySubscriptChecker.cpp
UndefinedAssignmentChecker.cpp
UnixAPIChecker.cpp
UnreachableCodeChecker.cpp
VforkChecker.cpp
VLASizeChecker.cpp
ValistChecker.cpp
VirtualCallChecker.cpp
DEPENDS
ClangSACheckers
LINK_LIBS
clangAST
clangASTMatchers
clangAnalysis
clangBasic
clangLex
clangStaticAnalyzerCore
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,189 @@
//== 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.
//
//===----------------------------------------------------------------------===//
//
// An AST checker that looks for common pitfalls when using C string APIs.
// - Identifies erroneous patterns in the last argument to strncat - the number
// of bytes to copy.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TypeTraits.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
class WalkAST: public StmtVisitor<WalkAST> {
const CheckerBase *Checker;
BugReporter &BR;
AnalysisDeclContext* AC;
/// Check if two expressions refer to the same declaration.
bool sameDecl(const Expr *A1, const Expr *A2) {
if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
return D1->getDecl() == D2->getDecl();
return false;
}
/// Check if the expression E is a sizeof(WithArg).
bool isSizeof(const Expr *E, const Expr *WithArg) {
if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType())
return sameDecl(UE->getArgumentExpr(), WithArg);
return false;
}
/// Check if the expression E is a strlen(WithArg).
bool isStrlen(const Expr *E, const Expr *WithArg) {
if (const auto *CE = dyn_cast<CallExpr>(E)) {
const FunctionDecl *FD = CE->getDirectCallee();
if (!FD)
return false;
return (CheckerContext::isCLibraryFunction(FD, "strlen") &&
sameDecl(CE->getArg(0), WithArg));
}
return false;
}
/// Check if the expression is an integer literal with value 1.
bool isOne(const Expr *E) {
if (const auto *IL = dyn_cast<IntegerLiteral>(E))
return (IL->getValue().isIntN(1));
return false;
}
StringRef getPrintableName(const Expr *E) {
if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
return D->getDecl()->getName();
return StringRef();
}
/// Identify erroneous patterns in the last argument to strncat - the number
/// of bytes to copy.
bool containsBadStrncatPattern(const CallExpr *CE);
public:
WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
: Checker(Checker), BR(BR), AC(AC) {}
// Statement visitor methods.
void VisitChildren(Stmt *S);
void VisitStmt(Stmt *S) {
VisitChildren(S);
}
void VisitCallExpr(CallExpr *CE);
};
} // end anonymous namespace
// The correct size argument should look like following:
// strncat(dst, src, sizeof(dst) - strlen(dest) - 1);
// We look for the following anti-patterns:
// - strncat(dst, src, sizeof(dst) - strlen(dst));
// - strncat(dst, src, sizeof(dst) - 1);
// - strncat(dst, src, sizeof(dst));
bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
if (CE->getNumArgs() != 3)
return false;
const Expr *DstArg = CE->getArg(0);
const Expr *SrcArg = CE->getArg(1);
const Expr *LenArg = CE->getArg(2);
// Identify wrong size expressions, which are commonly used instead.
if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
// - sizeof(dst) - strlen(dst)
if (BE->getOpcode() == BO_Sub) {
const Expr *L = BE->getLHS();
const Expr *R = BE->getRHS();
if (isSizeof(L, DstArg) && isStrlen(R, DstArg))
return true;
// - sizeof(dst) - 1
if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts()))
return true;
}
}
// - sizeof(dst)
if (isSizeof(LenArg, DstArg))
return true;
// - sizeof(src)
if (isSizeof(LenArg, SrcArg))
return true;
return false;
}
void WalkAST::VisitCallExpr(CallExpr *CE) {
const FunctionDecl *FD = CE->getDirectCallee();
if (!FD)
return;
if (CheckerContext::isCLibraryFunction(FD, "strncat")) {
if (containsBadStrncatPattern(CE)) {
const Expr *DstArg = CE->getArg(0);
const Expr *LenArg = CE->getArg(2);
PathDiagnosticLocation Loc =
PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
StringRef DstName = getPrintableName(DstArg);
SmallString<256> S;
llvm::raw_svector_ostream os(S);
os << "Potential buffer overflow. ";
if (!DstName.empty()) {
os << "Replace with 'sizeof(" << DstName << ") "
"- strlen(" << DstName <<") - 1'";
os << " or u";
} else
os << "U";
os << "se a safer 'strlcat' API";
BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
"C String API", os.str(), Loc,
LenArg->getSourceRange());
}
}
// Recurse and check children.
VisitChildren(CE);
}
void WalkAST::VisitChildren(Stmt *S) {
for (Stmt *Child : S->children())
if (Child)
Visit(Child);
}
namespace {
class CStringSyntaxChecker: public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
BugReporter &BR) const {
WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
walker.Visit(D->getBody());
}
};
}
void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
mgr.registerChecker<CStringSyntaxChecker>();
}

View File

@ -0,0 +1,62 @@
//=== CXXSelfAssignmentChecker.cpp -----------------------------*- 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 CXXSelfAssignmentChecker, which tests all custom defined
// copy and move assignment operators for the case of self assignment, thus
// where the parameter refers to the same location where the this pointer
// points to. The checker itself does not do any checks at all, but it
// causes the analyzer to check every copy and move assignment operator twice:
// once for when 'this' aliases with the parameter and once for when it may not.
// It is the task of the other enabled checkers to find the bugs in these two
// different cases.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class CXXSelfAssignmentChecker : public Checker<check::BeginFunction> {
public:
CXXSelfAssignmentChecker();
void checkBeginFunction(CheckerContext &C) const;
};
}
CXXSelfAssignmentChecker::CXXSelfAssignmentChecker() {}
void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const {
if (!C.inTopFrame())
return;
const auto *LCtx = C.getLocationContext();
const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
if (!MD)
return;
if (!MD->isCopyAssignmentOperator() && !MD->isMoveAssignmentOperator())
return;
auto &State = C.getState();
auto &SVB = C.getSValBuilder();
auto ThisVal =
State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
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);
ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx);
C.addTransition(NonSelfAssignState);
}
void ento::registerCXXSelfAssignmentChecker(CheckerManager &Mgr) {
Mgr.registerChecker<CXXSelfAssignmentChecker>();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,149 @@
//=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// CastSizeChecker checks when casting a malloc'ed symbolic region to type T,
// whether the size of the symbolic region is a multiple of the size of T.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/CharUnits.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/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
mutable std::unique_ptr<BuiltinBug> BT;
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
};
}
/// Check if we are casting to a struct with a flexible array at the end.
/// \code
/// struct foo {
/// size_t len;
/// struct bar data[];
/// };
/// \endcode
/// or
/// \code
/// struct foo {
/// size_t len;
/// struct bar data[0];
/// }
/// \endcode
/// In these cases it is also valid to allocate size of struct foo + a multiple
/// of struct bar.
static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
CharUnits TypeSize, QualType ToPointeeTy) {
const RecordType *RT = ToPointeeTy->getAs<RecordType>();
if (!RT)
return false;
const RecordDecl *RD = RT->getDecl();
RecordDecl::field_iterator Iter(RD->field_begin());
RecordDecl::field_iterator End(RD->field_end());
const FieldDecl *Last = nullptr;
for (; Iter != End; ++Iter)
Last = *Iter;
assert(Last && "empty structs should already be handled");
const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
CharUnits FlexSize;
if (const ConstantArrayType *ArrayTy =
Ctx.getAsConstantArrayType(Last->getType())) {
FlexSize = Ctx.getTypeSizeInChars(ElemType);
if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
TypeSize -= FlexSize;
else if (ArrayTy->getSize() != 0)
return false;
} else if (RD->hasFlexibleArrayMember()) {
FlexSize = Ctx.getTypeSizeInChars(ElemType);
} else {
return false;
}
if (FlexSize.isZero())
return false;
CharUnits Left = RegionSize - TypeSize;
if (Left.isNegative())
return false;
return Left % FlexSize == 0;
}
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
const Expr *E = CE->getSubExpr();
ASTContext &Ctx = C.getASTContext();
QualType ToTy = Ctx.getCanonicalType(CE->getType());
const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());
if (!ToPTy)
return;
QualType ToPointeeTy = ToPTy->getPointeeType();
// Only perform the check if 'ToPointeeTy' is a complete type.
if (ToPointeeTy->isIncompleteType())
return;
ProgramStateRef state = C.getState();
const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion();
if (!R)
return;
const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R);
if (!SR)
return;
SValBuilder &svalBuilder = C.getSValBuilder();
SVal extent = SR->getExtent(svalBuilder);
const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent);
if (!extentInt)
return;
CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue());
CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy);
// Ignore void, and a few other un-sizeable types.
if (typeSize.isZero())
return;
if (regionSize % typeSize == 0)
return;
if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
return;
if (ExplodedNode *errorNode = C.generateErrorNode()) {
if (!BT)
BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
"Cast a region whose size is not a multiple"
" of the destination type size."));
auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode);
R->addRange(CE->getSourceRange());
C.emitReport(std::move(R));
}
}
void ento::registerCastSizeChecker(CheckerManager &mgr) {
// 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>();
}

View File

@ -0,0 +1,122 @@
//=== CastToStructChecker.cpp ----------------------------------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This files defines CastToStructChecker, a builtin checker that checks for
// cast from non-struct pointer to struct pointer and widening struct data cast.
// This check corresponds to CWE-588.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/RecursiveASTVisitor.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/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class CastToStructVisitor : public RecursiveASTVisitor<CastToStructVisitor> {
BugReporter &BR;
const CheckerBase *Checker;
AnalysisDeclContext *AC;
public:
explicit CastToStructVisitor(BugReporter &B, const CheckerBase *Checker,
AnalysisDeclContext *A)
: BR(B), Checker(Checker), AC(A) {}
bool VisitCastExpr(const CastExpr *CE);
};
}
bool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) {
const Expr *E = CE->getSubExpr();
ASTContext &Ctx = AC->getASTContext();
QualType OrigTy = Ctx.getCanonicalType(E->getType());
QualType ToTy = Ctx.getCanonicalType(CE->getType());
const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr());
const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());
if (!ToPTy || !OrigPTy)
return true;
QualType OrigPointeeTy = OrigPTy->getPointeeType();
QualType ToPointeeTy = ToPTy->getPointeeType();
if (!ToPointeeTy->isStructureOrClassType())
return true;
// We allow cast from void*.
if (OrigPointeeTy->isVoidType())
return true;
// Now the cast-to-type is struct pointer, the original type is not void*.
if (!OrigPointeeTy->isRecordType()) {
SourceRange Sr[1] = {CE->getSourceRange()};
PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport(
AC->getDecl(), Checker, "Cast from non-struct type to struct type",
categories::LogicError, "Casting a non-structure type to a structure "
"type and accessing a field can lead to memory "
"access errors or data corruption.",
Loc, Sr);
} else {
// Don't warn when size of data is unknown.
const auto *U = dyn_cast<UnaryOperator>(E);
if (!U || U->getOpcode() != UO_AddrOf)
return true;
// Don't warn for references
const ValueDecl *VD = nullptr;
if (const auto *SE = dyn_cast<DeclRefExpr>(U->getSubExpr()))
VD = dyn_cast<ValueDecl>(SE->getDecl());
else if (const auto *SE = dyn_cast<MemberExpr>(U->getSubExpr()))
VD = SE->getMemberDecl();
if (!VD || VD->getType()->isReferenceType())
return true;
if (ToPointeeTy->isIncompleteType() ||
OrigPointeeTy->isIncompleteType())
return true;
// Warn when there is widening cast.
unsigned ToWidth = Ctx.getTypeInfo(ToPointeeTy).Width;
unsigned OrigWidth = Ctx.getTypeInfo(OrigPointeeTy).Width;
if (ToWidth <= OrigWidth)
return true;
PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport(AC->getDecl(), Checker, "Widening cast to struct type",
categories::LogicError,
"Casting data to a larger structure type and accessing "
"a field can lead to memory access errors or data "
"corruption.",
Loc, CE->getSourceRange());
}
return true;
}
namespace {
class CastToStructChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
BugReporter &BR) const {
CastToStructVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
Visitor.TraverseDecl(const_cast<Decl *>(D));
}
};
} // end anonymous namespace
void ento::registerCastToStructChecker(CheckerManager &mgr) {
mgr.registerChecker<CastToStructChecker>();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- 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 a CheckObjCInstMethSignature, a flow-insenstive check
// that determines if an Objective-C class interface incorrectly redefines
// the method signature in a subclass.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
ASTContext &C) {
// Right now don't compare the compatibility of pointers. That involves
// looking at subtyping relationships. FIXME: Future patch.
if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType())
return true;
return C.typesAreCompatible(Derived, Ancestor);
}
static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
const ObjCMethodDecl *MethAncestor,
BugReporter &BR, ASTContext &Ctx,
const ObjCImplementationDecl *ID,
const CheckerBase *Checker) {
QualType ResDerived = MethDerived->getReturnType();
QualType ResAncestor = MethAncestor->getReturnType();
if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
os << "The Objective-C class '"
<< *MethDerived->getClassInterface()
<< "', which is derived from class '"
<< *MethAncestor->getClassInterface()
<< "', defines the instance method '";
MethDerived->getSelector().print(os);
os << "' whose return type is '"
<< ResDerived.getAsString()
<< "'. A method with the same name (same selector) is also defined in "
"class '"
<< *MethAncestor->getClassInterface()
<< "' and has a return type of '"
<< ResAncestor.getAsString()
<< "'. These two types are incompatible, and may result in undefined "
"behavior for clients of these classes.";
PathDiagnosticLocation MethDLoc =
PathDiagnosticLocation::createBegin(MethDerived,
BR.getSourceManager());
BR.EmitBasicReport(
MethDerived, Checker, "Incompatible instance method return type",
categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
}
}
static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
BugReporter &BR,
const CheckerBase *Checker) {
const ObjCInterfaceDecl *D = ID->getClassInterface();
const ObjCInterfaceDecl *C = D->getSuperClass();
if (!C)
return;
ASTContext &Ctx = BR.getContext();
// Build a DenseMap of the methods for quick querying.
typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
MapTy IMeths;
unsigned NumMethods = 0;
for (auto *M : ID->instance_methods()) {
IMeths[M->getSelector()] = M;
++NumMethods;
}
// Now recurse the class hierarchy chain looking for methods with the
// same signatures.
while (C && NumMethods) {
for (const auto *M : C->instance_methods()) {
Selector S = M->getSelector();
MapTy::iterator MI = IMeths.find(S);
if (MI == IMeths.end() || MI->second == nullptr)
continue;
--NumMethods;
ObjCMethodDecl *MethDerived = MI->second;
MI->second = nullptr;
CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
}
C = C->getSuperClass();
}
}
//===----------------------------------------------------------------------===//
// ObjCMethSigsChecker
//===----------------------------------------------------------------------===//
namespace {
class ObjCMethSigsChecker : public Checker<
check::ASTDecl<ObjCImplementationDecl> > {
public:
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
BugReporter &BR) const {
CheckObjCInstMethSignature(D, BR, this);
}
};
}
void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCMethSigsChecker>();
}

Some files were not shown because too many files have changed in this diff Show More