diff options
author | dim <dim@FreeBSD.org> | 2011-02-20 13:06:31 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2011-02-20 13:06:31 +0000 |
commit | 39fcc9a984e2820e4ea0fa2ac4abd17d9f3a31df (patch) | |
tree | a9243275843fbeaa590afc07ee888e006b8d54ea /lib/StaticAnalyzer/Checkers | |
parent | 69b4eca4a4255ba43baa5c1d9bbdec3ec17f479e (diff) | |
download | FreeBSD-src-39fcc9a984e2820e4ea0fa2ac4abd17d9f3a31df.zip FreeBSD-src-39fcc9a984e2820e4ea0fa2ac4abd17d9f3a31df.tar.gz |
Vendor import of clang trunk r126079:
http://llvm.org/svn/llvm-project/cfe/trunk@126079
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
58 files changed, 14748 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp new file mode 100644 index 0000000..8832b05 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp @@ -0,0 +1,96 @@ +//== AdjustedReturnValueChecker.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 AdjustedReturnValueChecker, a simple check to see if the +// return value of a function call is different than the one the caller thinks +// it is. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class AdjustedReturnValueChecker : + public CheckerVisitor<AdjustedReturnValueChecker> { +public: + AdjustedReturnValueChecker() {} + + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + static void *getTag() { + static int x = 0; return &x; + } +}; +} + +void ento::RegisterAdjustedReturnValueChecker(ExprEngine &Eng) { + Eng.registerCheck(new AdjustedReturnValueChecker()); +} + +void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + + // Get the result type of the call. + QualType expectedResultTy = CE->getType(); + + // Fetch the signature of the called function. + const GRState *state = C.getState(); + + SVal V = state->getSVal(CE); + + if (V.isUnknown()) + return; + + // Casting to void? Discard the value. + if (expectedResultTy->isVoidType()) { + C.generateNode(state->BindExpr(CE, UnknownVal())); + return; + } + + const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion(); + if (!callee) + return; + + QualType actualResultTy; + + if (const FunctionTextRegion *FT = dyn_cast<FunctionTextRegion>(callee)) { + const FunctionDecl *FD = FT->getDecl(); + actualResultTy = FD->getResultType(); + } + else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) { + const BlockTextRegion *BR = BD->getCodeRegion(); + const BlockPointerType *BT=BR->getLocationType()->getAs<BlockPointerType>(); + const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>(); + actualResultTy = FT->getResultType(); + } + + // Can this happen? + if (actualResultTy.isNull()) + return; + + // For now, ignore references. + if (actualResultTy->getAs<ReferenceType>()) + return; + + + // Are they the same? + if (expectedResultTy != actualResultTy) { + // FIXME: Do more checking and actual emit an error. At least performing + // the cast avoids some assertion failures elsewhere. + SValBuilder &svalBuilder = C.getSValBuilder(); + V = svalBuilder.evalCast(V, expectedResultTy, actualResultTy); + C.generateNode(state->BindExpr(CE, V)); + } +} diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp new file mode 100644 index 0000000..7b68887 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -0,0 +1,123 @@ +//==--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 "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" + +// FIXME: Restructure checker registration. +#include "ExperimentalChecks.h" + +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; +using namespace ento; + +namespace { +class AnalyzerStatsChecker : public CheckerVisitor<AnalyzerStatsChecker> { +public: + static void *getTag(); + void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng); + +private: + llvm::SmallPtrSet<const CFGBlock*, 256> reachable; +}; +} + +void *AnalyzerStatsChecker::getTag() { + static int x = 0; + return &x; +} + +void ento::RegisterAnalyzerStatsChecker(ExprEngine &Eng) { + Eng.registerCheck(new AnalyzerStatsChecker()); +} + +void AnalyzerStatsChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng) { + const CFG *C = 0; + const Decl *D = 0; + const LocationContext *LC = 0; + const SourceManager &SM = B.getSourceManager(); + + // Iterate over explodedgraph + for (ExplodedGraph::node_iterator I = G.nodes_begin(); + I != G.nodes_end(); ++I) { + const ProgramPoint &P = I->getLocation(); + // Save the LocationContext if we don't have it already + if (!LC) + LC = P.getLocationContext(); + + if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB); + } + } + + // Get the CFG and the Decl of this block + C = LC->getCFG(); + D = LC->getAnalysisContext()->getDecl(); + + 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--; + + // Generate the warning string + llvm::SmallString<128> buf; + llvm::raw_svector_ostream output(buf); + PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); + if (Loc.isValid()) { + output << Loc.getFilename() << " : "; + + 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(); + } + } + + output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: " + << unreachable << " | Aborted Block: " + << (Eng.wasBlockAborted() ? "yes" : "no") + << " | Empty WorkList: " + << (Eng.hasEmptyWorkList() ? "yes" : "no"); + + B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(), + D->getLocation()); + + // Emit warning for each block we bailed out on + typedef CoreEngine::BlocksAborted::const_iterator AbortedIterator; + const CoreEngine &CE = Eng.getCoreEngine(); + for (AbortedIterator I = CE.blocks_aborted_begin(), + E = CE.blocks_aborted_end(); I != E; ++I) { + const BlockEdge &BE = I->first; + const CFGBlock *Exit = BE.getDst(); + const CFGElement &CE = Exit->front(); + if (const CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) + B.EmitBasicReport("Bailout Point", "Internal Statistics", "The analyzer " + "stopped analyzing at this point", CS->getStmt()->getLocStart()); + } +} diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp new file mode 100644 index 0000000..9194791 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -0,0 +1,92 @@ +//== 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 "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ArrayBoundChecker : + public CheckerVisitor<ArrayBoundChecker> { + BuiltinBug *BT; +public: + ArrayBoundChecker() : BT(0) {} + static void *getTag() { static int x = 0; return &x; } + void visitLocation(CheckerContext &C, const Stmt *S, SVal l, bool isLoad); +}; +} + +void ento::RegisterArrayBoundChecker(ExprEngine &Eng) { + Eng.registerCheck(new ArrayBoundChecker()); +} + +void ArrayBoundChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l, + bool isLoad) { + // 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 = cast<DefinedOrUnknownSVal>(ER->getIndex()); + + // Zero index is always in bound, this also passes ElementRegions created for + // pointer casts. + if (Idx.isZeroConstant()) + return; + + const GRState *state = C.getState(); + + // Get the size of the array. + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType()); + + const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(StOutBound); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("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. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + C.EmitReport(report); + return; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + assert(StInBound); + C.addTransition(StInBound); +} diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp new file mode 100644 index 0000000..f803d27 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -0,0 +1,277 @@ +//== 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 "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/AST/CharUnits.h" + +using namespace clang; +using namespace ento; + +namespace { +class ArrayBoundCheckerV2 : + public CheckerVisitor<ArrayBoundCheckerV2> { + BuiltinBug *BT; + + enum OOB_Kind { OOB_Precedes, OOB_Excedes }; + + void reportOOB(CheckerContext &C, const GRState *errorState, + OOB_Kind kind); + +public: + ArrayBoundCheckerV2() : BT(0) {} + static void *getTag() { static int x = 0; return &x; } + void visitLocation(CheckerContext &C, const Stmt *S, SVal l, bool isLoad); +}; + +// FIXME: Eventually replace RegionRawOffset with this class. +class RegionRawOffsetV2 { +private: + const SubRegion *baseRegion; + SVal byteOffset; + + RegionRawOffsetV2() + : baseRegion(0), byteOffset(UnknownVal()) {} + +public: + RegionRawOffsetV2(const SubRegion* base, SVal offset) + : baseRegion(base), byteOffset(offset) {} + + NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); } + const SubRegion *getRegion() const { return baseRegion; } + + static RegionRawOffsetV2 computeOffset(const GRState *state, + SValBuilder &svalBuilder, + SVal location); + + void dump() const; + void dumpToStream(llvm::raw_ostream& os) const; +}; +} + +void ento::RegisterArrayBoundCheckerV2(ExprEngine &Eng) { + Eng.registerCheck(new ArrayBoundCheckerV2()); +} + +void ArrayBoundCheckerV2::visitLocation(CheckerContext &checkerContext, + const Stmt *S, + SVal location, bool isLoad) { + + // NOTE: Instead of using GRState::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. + const GRState *state = checkerContext.getState(); + const GRState *originalState = state; + + SValBuilder &svalBuilder = checkerContext.getSValBuilder(); + const RegionRawOffsetV2 &rawOffset = + RegionRawOffsetV2::computeOffset(state, svalBuilder, location); + + if (!rawOffset.getRegion()) + return; + + // CHECK LOWER BOUND: Is byteOffset < 0? If so, we are doing a load/store + // before the first valid offset in the memory region. + + SVal lowerBound + = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), + svalBuilder.makeZeroArrayIndex(), + svalBuilder.getConditionType()); + + NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound); + if (!lowerBoundToCheck) + return; + + const GRState *state_precedesLowerBound, *state_withinLowerBound; + llvm::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 (!isa<NonLoc>(extentVal)) + break; + + SVal upperbound + = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(), + cast<NonLoc>(extentVal), + svalBuilder.getConditionType()); + + NonLoc *upperboundToCheck = dyn_cast<NonLoc>(&upperbound); + if (!upperboundToCheck) + break; + + const GRState *state_exceedsUpperBound, *state_withinUpperBound; + llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = + state->assume(*upperboundToCheck); + + // Are we constrained enough to definitely exceed the upper bound? + if (state_exceedsUpperBound && !state_withinUpperBound) { + reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); + return; + } + + assert(state_withinUpperBound); + state = state_withinUpperBound; + } + while (false); + + if (state != originalState) + checkerContext.generateNode(state); +} + +void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, + const GRState *errorState, + OOB_Kind kind) { + + ExplodedNode *errorNode = checkerContext.generateSink(errorState); + if (!errorNode) + return; + + if (!BT) + BT = new BuiltinBug("Out-of-bound access"); + + // FIXME: This diagnostics are preliminary. We should get far better + // diagnostics for explaining buffer overruns. + + llvm::SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Out of bound memory access " + << (kind == OOB_Precedes ? "(accessed memory precedes memory block)" + : "(access exceeds upper limit of memory block)"); + + checkerContext.EmitReport(new RangedBugReport(*BT, os.str(), errorNode)); +} + +void RegionRawOffsetV2::dump() const { + dumpToStream(llvm::errs()); +} + +void RegionRawOffsetV2::dumpToStream(llvm::raw_ostream& os) const { + os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; +} + +// FIXME: Merge with the implementation of the same method in Store.cpp +static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { + if (const RecordType *RT = Ty->getAs<RecordType>()) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition()) + return false; + } + + return true; +} + + +// 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 isa<UndefinedVal>(val) ? 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(const GRState *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(const GRState *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, + cast<NonLoc>(x), cast<NonLoc>(y), + svalBuilder.getArrayIndexType()); +} + +/// Compute a raw byte offset from a base region. Used for array bounds +/// checking. +RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const GRState *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)) + if (!offset.isUnknownOrUndef()) + return RegionRawOffsetV2(subReg, offset); + return RegionRawOffsetV2(); + } + case MemRegion::ElementRegionKind: { + const ElementRegion *elemReg = cast<ElementRegion>(region); + SVal index = elemReg->getIndex(); + if (!isa<NonLoc>(index)) + return RegionRawOffsetV2(); + QualType elemType = elemReg->getElementType(); + // If the element is an incomplete type, go no further. + ASTContext &astContext = svalBuilder.getContext(); + if (!IsCompleteType(astContext, elemType)) + return RegionRawOffsetV2(); + + // Update the offset. + offset = addValue(state, + getValue(offset, svalBuilder), + scaleValue(state, + cast<NonLoc>(index), + astContext.getTypeSizeInChars(elemType), + svalBuilder), + svalBuilder); + + if (offset.isUnknownOrUndef()) + return RegionRawOffsetV2(); + + region = elemReg->getSuperRegion(); + continue; + } + } + } + return RegionRawOffsetV2(); +} + + + diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp new file mode 100644 index 0000000..e4865b1 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -0,0 +1,136 @@ +//===--- AttrNonNullChecker.h - 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. +// +//===----------------------------------------------------------------------===// +// +// This defines AttrNonNullChecker, a builtin check in ExprEngine that +// performs checks for arguments declared to have nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class AttrNonNullChecker + : public CheckerVisitor<AttrNonNullChecker> { + BugType *BT; +public: + AttrNonNullChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +void ento::RegisterAttrNonNullChecker(ExprEngine &Eng) { + Eng.registerCheck(new AttrNonNullChecker()); +} + +void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + + // Check if the callee has a 'nonnull' attribute. + SVal X = state->getSVal(CE->getCallee()); + + const FunctionDecl* FD = X.getAsFunctionDecl(); + if (!FD) + return; + + const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); + if (!Att) + return; + + // Iterate through the arguments of CE and check them for null. + unsigned idx = 0; + + for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; + ++I, ++idx) { + + if (!Att->isNonNull(idx)) + continue; + + SVal V = state->getSVal(*I); + DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); + + // If the value is unknown or undefined, we can't perform this check. + if (!DV) + continue; + + if (!isa<Loc>(*DV)) { + // If the argument is a union type, we want to handle a potential + // transparent_unoin GCC extension. + QualType T = (*I)->getType(); + const RecordType *UT = T->getAsUnionType(); + if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) + continue; + if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) { + nonloc::CompoundVal::iterator CSV_I = CSV->begin(); + assert(CSV_I != CSV->end()); + V = *CSV_I; + DV = dyn_cast<DefinedSVal>(&V); + assert(++CSV_I == CSV->end()); + if (!DV) + continue; + } + else { + // FIXME: Handle LazyCompoundVals? + continue; + } + } + + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + if (stateNull && !stateNotNull) { + // Generate an error node. Check for a null node in case + // we cache out. + if (ExplodedNode *errorNode = C.generateSink(stateNull)) { + + // Lazily allocate the BugType object if it hasn't already been + // created. Ownership is transferred to the BugReporter object once + // the BugReport is passed to 'EmitWarning'. + if (!BT) + BT = new BugType("Argument with 'nonnull' attribute passed null", + "API"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, + "Null pointer passed as an argument to a " + "'nonnull' parameter", errorNode); + + // Highlight the range of the argument that was null. + const Expr *arg = *I; + R->addRange(arg->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); + + // Emit the bug report. + C.EmitReport(R); + } + + // Always return. Either we cached out or we just emitted an error. + return; + } + + // If a pointer value passed the check we should assume that it is + // indeed not null from this point forward. + assert(stateNotNull); + state = stateNotNull; + } + + // If we reach here all of the arguments passed the nonnull check. + // If 'state' has been updated generated a new node. + C.addTransition(state); +} diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp new file mode 100644 index 0000000..1b6c528 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -0,0 +1,521 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#include "BasicObjCFoundationChecks.h" + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ASTContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class APIMisuse : public BugType { +public: + APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) { + if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) + return ID->getTypeForDecl()->getAs<ObjCInterfaceType>(); + return NULL; +} + +static const char* GetReceiverNameType(const ObjCMessage &msg) { + if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg)) + return ReceiverType->getDecl()->getIdentifier()->getNameStart(); + return NULL; +} + +static bool isNSString(llvm::StringRef ClassName) { + return ClassName == "NSString" || ClassName == "NSMutableString"; +} + +static inline bool isNil(SVal X) { + return isa<loc::ConcreteInt>(X); +} + +//===----------------------------------------------------------------------===// +// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. +//===----------------------------------------------------------------------===// + +namespace { + class NilArgChecker : public CheckerVisitor<NilArgChecker> { + APIMisuse *BT; + void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg); + public: + NilArgChecker() : BT(0) {} + static void *getTag() { static int x = 0; return &x; } + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); + }; +} + +void NilArgChecker::WarnNilArg(CheckerContext &C, + const ObjCMessage &msg, + unsigned int Arg) +{ + if (!BT) + BT = new APIMisuse("nil argument"); + + if (ExplodedNode *N = C.generateSink()) { + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Argument to '" << GetReceiverNameType(msg) << "' method '" + << msg.getSelector().getAsString() << "' cannot be nil"; + + RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); + R->addRange(msg.getArgSourceRange(Arg)); + C.EmitReport(R); + } +} + +void NilArgChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) +{ + const ObjCInterfaceType *ReceiverType = GetReceiverType(msg); + if (!ReceiverType) + return; + + if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { + Selector S = msg.getSelector(); + + if (S.isUnarySelector()) + return; + + // FIXME: This is going to be really slow doing these checks with + // lexical comparisons. + + std::string NameStr = S.getAsString(); + llvm::StringRef Name(NameStr); + assert(!Name.empty()); + + // FIXME: Checking for initWithFormat: will not work in most cases + // yet because [NSString alloc] returns id, not NSString*. We will + // need support for tracking expected-type information in the analyzer + // to find these errors. + if (Name == "caseInsensitiveCompare:" || + Name == "compare:" || + Name == "compare:options:" || + Name == "compare:options:range:" || + Name == "compare:options:range:locale:" || + Name == "componentsSeparatedByCharactersInSet:" || + Name == "initWithFormat:") { + if (isNil(msg.getArgSVal(0, C.getState()))) + WarnNilArg(C, msg, 0); + } + } +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +namespace { +class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> { + APIMisuse* BT; + IdentifierInfo* II; +public: + CFNumberCreateChecker() : BT(0), II(0) {} + ~CFNumberCreateChecker() {} + static void *getTag() { static int x = 0; return &x; } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +private: + void EmitError(const TypedRegion* R, const Expr* Ex, + uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); +}; +} // end anonymous namespace + +enum CFNumberType { + kCFNumberSInt8Type = 1, + kCFNumberSInt16Type = 2, + kCFNumberSInt32Type = 3, + kCFNumberSInt64Type = 4, + kCFNumberFloat32Type = 5, + kCFNumberFloat64Type = 6, + kCFNumberCharType = 7, + kCFNumberShortType = 8, + kCFNumberIntType = 9, + kCFNumberLongType = 10, + kCFNumberLongLongType = 11, + kCFNumberFloatType = 12, + kCFNumberDoubleType = 13, + kCFNumberCFIndexType = 14, + kCFNumberNSIntegerType = 15, + kCFNumberCGFloatType = 16 +}; + +namespace { + template<typename T> + class Optional { + bool IsKnown; + T Val; + public: + Optional() : IsKnown(false), Val(0) {} + Optional(const T& val) : IsKnown(true), Val(val) {} + + bool isKnown() const { return IsKnown; } + + const T& getValue() const { + assert (isKnown()); + return Val; + } + + operator const T&() const { + return getValue(); + } + }; +} + +static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { + static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; + + if (i < kCFNumberCharType) + return FixedSize[i-1]; + + QualType T; + + switch (i) { + case kCFNumberCharType: T = Ctx.CharTy; break; + case kCFNumberShortType: T = Ctx.ShortTy; break; + case kCFNumberIntType: T = Ctx.IntTy; break; + case kCFNumberLongType: T = Ctx.LongTy; break; + case kCFNumberLongLongType: T = Ctx.LongLongTy; break; + case kCFNumberFloatType: T = Ctx.FloatTy; break; + case kCFNumberDoubleType: T = Ctx.DoubleTy; break; + case kCFNumberCFIndexType: + case kCFNumberNSIntegerType: + case kCFNumberCGFloatType: + // FIXME: We need a way to map from names to Type*. + default: + return Optional<uint64_t>(); + } + + return Ctx.getTypeSize(T); +} + +#if 0 +static const char* GetCFNumberTypeStr(uint64_t i) { + static const char* Names[] = { + "kCFNumberSInt8Type", + "kCFNumberSInt16Type", + "kCFNumberSInt32Type", + "kCFNumberSInt64Type", + "kCFNumberFloat32Type", + "kCFNumberFloat64Type", + "kCFNumberCharType", + "kCFNumberShortType", + "kCFNumberIntType", + "kCFNumberLongType", + "kCFNumberLongLongType", + "kCFNumberFloatType", + "kCFNumberDoubleType", + "kCFNumberCFIndexType", + "kCFNumberNSIntegerType", + "kCFNumberCGFloatType" + }; + + return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; +} +#endif + +void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE) +{ + const Expr* Callee = CE->getCallee(); + const GRState *state = C.getState(); + SVal CallV = state->getSVal(Callee); + const FunctionDecl* FD = CallV.getAsFunctionDecl(); + + if (!FD) + return; + + ASTContext &Ctx = C.getASTContext(); + if (!II) + II = &Ctx.Idents.get("CFNumberCreate"); + + if (FD->getIdentifier() != II || CE->getNumArgs() != 3) + return; + + // Get the value of the "theType" argument. + SVal TheTypeVal = state->getSVal(CE->getArg(1)); + + // FIXME: We really should allow ranges of valid theType values, and + // bifurcate the state appropriately. + nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); + if (!V) + return; + + uint64_t NumberKind = V->getValue().getLimitedValue(); + Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); + + // FIXME: In some cases we can emit an error. + if (!TargetSize.isKnown()) + return; + + // Look at the value of the integer being passed by reference. Essentially + // we want to catch cases where the value passed in is not equal to the + // size of the type being created. + SVal TheValueExpr = state->getSVal(CE->getArg(2)); + + // FIXME: Eventually we should handle arbitrary locations. We can do this + // by having an enhanced memory model that does low-level typing. + loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); + if (!LV) + return; + + const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts()); + if (!R) + return; + + QualType T = Ctx.getCanonicalType(R->getValueType()); + + // FIXME: If the pointee isn't an integer type, should we flag a warning? + // People can do weird stuff with pointers. + + if (!T->isIntegerType()) + return; + + uint64_t SourceSize = Ctx.getTypeSize(T); + + // CHECK: is SourceSize == TargetSize + if (SourceSize == TargetSize) + return; + + // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; + // otherwise generate a regular node. + // + // FIXME: We can actually create an abstract "CFNumber" object that has + // the bits initialized to the provided values. + // + if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() + : C.generateNode()) { + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + + os << (SourceSize == 8 ? "An " : "A ") + << SourceSize << " bit integer is used to initialize a CFNumber " + "object that represents " + << (TargetSize == 8 ? "an " : "a ") + << TargetSize << " bit integer. "; + + if (SourceSize < TargetSize) + os << (TargetSize - SourceSize) + << " bits of the CFNumber value will be garbage." ; + else + os << (SourceSize - TargetSize) + << " bits of the input integer will be lost."; + + if (!BT) + BT = new APIMisuse("Bad use of CFNumberCreate"); + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(CE->getArg(2)->getSourceRange()); + C.EmitReport(report); + } +} + +//===----------------------------------------------------------------------===// +// CFRetain/CFRelease checking for null arguments. +//===----------------------------------------------------------------------===// + +namespace { +class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> { + APIMisuse *BT; + IdentifierInfo *Retain, *Release; +public: + CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {} + static void *getTag() { static int x = 0; return &x; } + void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE); +}; +} // end anonymous namespace + + +void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, + const CallExpr* CE) { + // If the CallExpr doesn't have exactly 1 argument just give up checking. + if (CE->getNumArgs() != 1) + return; + + // Get the function declaration of the callee. + const GRState* state = C.getState(); + SVal X = state->getSVal(CE->getCallee()); + const FunctionDecl* FD = X.getAsFunctionDecl(); + + if (!FD) + return; + + if (!BT) { + ASTContext &Ctx = C.getASTContext(); + Retain = &Ctx.Idents.get("CFRetain"); + Release = &Ctx.Idents.get("CFRelease"); + BT = new APIMisuse("null passed to CFRetain/CFRelease"); + } + + // Check if we called CFRetain/CFRelease. + const IdentifierInfo *FuncII = FD->getIdentifier(); + if (!(FuncII == Retain || FuncII == Release)) + return; + + // FIXME: The rest of this just checks that the argument is non-null. + // It should probably be refactored and combined with AttrNonNullChecker. + + // Get the argument's value. + const Expr *Arg = CE->getArg(0); + SVal ArgVal = state->getSVal(Arg); + DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); + if (!DefArgVal) + return; + + // Get a NULL value. + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); + + // Make an expression asserting that they're equal. + DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); + + // Are they equal? + const GRState *stateTrue, *stateFalse; + llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); + + if (stateTrue && !stateFalse) { + ExplodedNode *N = C.generateSink(stateTrue); + if (!N) + return; + + const char *description = (FuncII == Retain) + ? "Null pointer argument in call to CFRetain" + : "Null pointer argument in call to CFRelease"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); + report->addRange(Arg->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); + C.EmitReport(report); + return; + } + + // From here on, we know the argument is non-null. + C.addTransition(stateFalse); +} + +//===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> { + Selector releaseS; + Selector retainS; + Selector autoreleaseS; + Selector drainS; + BugType *BT; +public: + ClassReleaseChecker() + : BT(0) {} + + static void *getTag() { static int x = 0; return &x; } + + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); +}; +} + +void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { + + if (!BT) { + BT = new APIMisuse("message incorrectly sent to class instead of class " + "instance"); + + ASTContext &Ctx = C.getASTContext(); + releaseS = GetNullarySelector("release", Ctx); + retainS = GetNullarySelector("retain", Ctx); + autoreleaseS = GetNullarySelector("autorelease", Ctx); + drainS = GetNullarySelector("drain", Ctx); + } + + if (msg.isInstanceMessage()) + return; + const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); + assert(Class); + + Selector S = msg.getSelector(); + if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) + return; + + if (ExplodedNode *N = C.generateNode()) { + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "The '" << S.getAsString() << "' message should be sent to instances " + "of class '" << Class->getName() + << "' and not the class directly"; + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(msg.getSourceRange()); + C.EmitReport(report); + } +} + +//===----------------------------------------------------------------------===// +// Check registration. +//===----------------------------------------------------------------------===// + +static void RegisterNilArgChecker(ExprEngine& Eng) { + Eng.registerCheck(new NilArgChecker()); +} + +void ento::registerNilArgChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterNilArgChecker); +} + +static void RegisterCFNumberCreateChecker(ExprEngine& Eng) { + Eng.registerCheck(new CFNumberCreateChecker()); +} + +void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterCFNumberCreateChecker); +} + +static void RegisterCFRetainReleaseChecker(ExprEngine& Eng) { + Eng.registerCheck(new CFRetainReleaseChecker()); +} + +void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterCFRetainReleaseChecker); +} + +static void RegisterClassReleaseChecker(ExprEngine& Eng) { + Eng.registerCheck(new ClassReleaseChecker()); +} + +void ento::registerClassReleaseChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterClassReleaseChecker); +} diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.h b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.h new file mode 100644 index 0000000..92cfb1a --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.h @@ -0,0 +1,35 @@ +//== BasicObjCFoundationChecks.h - 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_BASICOBJCFOUNDATIONCHECKS +#define LLVM_CLANG_GR_BASICOBJCFOUNDATIONCHECKS + +namespace clang { + +class ASTContext; +class Decl; + +namespace ento { + +class BugReporter; +class ExprEngine; + +void RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, const Decl &D); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp new file mode 100644 index 0000000..417b015 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -0,0 +1,83 @@ +//=== 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 "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { + +class BuiltinFunctionChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void ento::RegisterBuiltinFunctionChecker(ExprEngine &Eng) { + Eng.registerCheck(new BuiltinFunctionChecker()); +} + +bool BuiltinFunctionChecker::evalCallExpr(CheckerContext &C,const CallExpr *CE){ + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + + if (!FD) + return false; + + unsigned id = FD->getBuiltinID(); + + if (!id) + return false; + + switch (id) { + case Builtin::BI__builtin_expect: { + // For __builtin_expect, just return the value of the subexpression. + assert (CE->arg_begin() != CE->arg_end()); + SVal X = state->getSVal(*(CE->arg_begin())); + C.generateNode(state->BindExpr(CE, X)); + return true; + } + + case Builtin::BI__builtin_alloca: { + // FIXME: Refactor into StoreManager itself? + MemRegionManager& RM = C.getStoreManager().getRegionManager(); + const AllocaRegion* R = + RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), + C.getPredecessor()->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 = + cast<DefinedOrUnknownSVal>(state->getSVal(*(CE->arg_begin()))); + + SValBuilder& svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal extentMatchesSizeArg = + svalBuilder.evalEQ(state, Extent, Size); + state = state->assume(extentMatchesSizeArg, true); + + C.generateNode(state->BindExpr(CE, loc::MemRegionVal(R))); + return true; + } + } + + return false; +} diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt new file mode 100644 index 0000000..e172a52 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -0,0 +1,70 @@ +set(LLVM_TARGET_DEFINITIONS Checkers.td) +tablegen(Checkers.inc + -gen-clang-sa-checkers + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../../include) +add_custom_target(ClangSACheckers + DEPENDS Checkers.inc) + +set(LLVM_USED_LIBS clangBasic clangAST) + +add_clang_library(clangStaticAnalyzerCheckers + AdjustedReturnValueChecker.cpp + AnalyzerStatsChecker.cpp + ArrayBoundChecker.cpp + ArrayBoundCheckerV2.cpp + AttrNonNullChecker.cpp + BasicObjCFoundationChecks.cpp + BuiltinFunctionChecker.cpp + CStringChecker.cpp + CallAndMessageChecker.cpp + CastSizeChecker.cpp + CastToStructChecker.cpp + CheckObjCDealloc.cpp + CheckObjCInstMethSignature.cpp + CheckSecuritySyntaxOnly.cpp + CheckSizeofPointer.cpp + ChrootChecker.cpp + ClangSACheckerProvider.cpp + DeadStoresChecker.cpp + DebugCheckers.cpp + DereferenceChecker.cpp + DivZeroChecker.cpp + ExprEngine.cpp + ExperimentalChecks.cpp + FixedAddressChecker.cpp + IdempotentOperationChecker.cpp + LLVMConventionsChecker.cpp + MacOSXAPIChecker.cpp + MallocChecker.cpp + NSAutoreleasePoolChecker.cpp + NSErrorChecker.cpp + NoReturnFunctionChecker.cpp + OSAtomicChecker.cpp + ObjCAtSyncChecker.cpp + ObjCSelfInitChecker.cpp + ObjCUnusedIVarsChecker.cpp + PointerArithChecker.cpp + PointerSubChecker.cpp + PthreadLockChecker.cpp + ReturnPointerRangeChecker.cpp + ReturnUndefChecker.cpp + StackAddrLeakChecker.cpp + StreamChecker.cpp + UndefBranchChecker.cpp + UndefCapturedBlockVarChecker.cpp + UndefResultChecker.cpp + UndefinedArraySubscriptChecker.cpp + UndefinedAssignmentChecker.cpp + UnixAPIChecker.cpp + UnreachableCodeChecker.cpp + VLASizeChecker.cpp + ) + +add_dependencies(clangStaticAnalyzerCheckers + clangStaticAnalyzerCore + ClangAttrClasses + ClangAttrList + ClangDeclNodes + ClangStmtNodes + ClangSACheckers + ) diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp new file mode 100644 index 0000000..03f9047 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -0,0 +1,1053 @@ +//= CStringChecker.h - 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. +// +//===----------------------------------------------------------------------===// +// +// This defines CStringChecker, which is an assortment of checks on calls +// to functions in <string.h>. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace ento; + +namespace { +class CStringChecker : public CheckerVisitor<CStringChecker> { + BugType *BT_Null, *BT_Bounds, *BT_BoundsWrite, *BT_Overlap, *BT_NotCString; +public: + CStringChecker() + : BT_Null(0), BT_Bounds(0), BT_BoundsWrite(0), BT_Overlap(0), BT_NotCString(0) + {} + static void *getTag() { static int tag; return &tag; } + + bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); + void MarkLiveSymbols(const GRState *state, SymbolReaper &SR); + void evalDeadSymbols(CheckerContext &C, SymbolReaper &SR); + bool wantsRegionChangeUpdate(const GRState *state); + + const GRState *EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool*); + + typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *); + + void evalMemcpy(CheckerContext &C, const CallExpr *CE); + void evalMemmove(CheckerContext &C, const CallExpr *CE); + void evalBcopy(CheckerContext &C, const CallExpr *CE); + void evalCopyCommon(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *Source, const Expr *Dest, + bool Restricted = false); + + void evalMemcmp(CheckerContext &C, const CallExpr *CE); + + void evalstrLength(CheckerContext &C, const CallExpr *CE); + + void evalStrcpy(CheckerContext &C, const CallExpr *CE); + void evalStpcpy(CheckerContext &C, const CallExpr *CE); + void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd); + + // Utility methods + std::pair<const GRState*, const GRState*> + assumeZero(CheckerContext &C, const GRState *state, SVal V, QualType Ty); + + const GRState *setCStringLength(const GRState *state, const MemRegion *MR, + SVal strLength); + SVal getCStringLengthForRegion(CheckerContext &C, const GRState *&state, + const Expr *Ex, const MemRegion *MR); + SVal getCStringLength(CheckerContext &C, const GRState *&state, + const Expr *Ex, SVal Buf); + + const GRState *InvalidateBuffer(CheckerContext &C, const GRState *state, + const Expr *Ex, SVal V); + + bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR); + + // Re-usable checks + const GRState *checkNonNull(CheckerContext &C, const GRState *state, + const Expr *S, SVal l); + const GRState *CheckLocation(CheckerContext &C, const GRState *state, + const Expr *S, SVal l, + bool IsDestination = false); + const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf = NULL, + bool FirstIsDestination = false); + const GRState *CheckOverlap(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *First, + const Expr *Second); + void emitOverlapBug(CheckerContext &C, const GRState *state, + const Stmt *First, const Stmt *Second); +}; + +class CStringLength { +public: + typedef llvm::ImmutableMap<const MemRegion *, SVal> EntryMap; +}; +} //end anonymous namespace + +namespace clang { +namespace ento { + template <> + struct GRStateTrait<CStringLength> + : public GRStatePartialTrait<CStringLength::EntryMap> { + static void *GDMIndex() { return CStringChecker::getTag(); } + }; +} +} + +static void RegisterCStringChecker(ExprEngine &Eng) { + Eng.registerCheck(new CStringChecker()); +} + +void ento::registerCStringChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterCStringChecker); +} + +//===----------------------------------------------------------------------===// +// Individual checks and utility methods. +//===----------------------------------------------------------------------===// + +std::pair<const GRState*, const GRState*> +CStringChecker::assumeZero(CheckerContext &C, const GRState *state, SVal V, + QualType Ty) { + DefinedSVal *val = dyn_cast<DefinedSVal>(&V); + if (!val) + return std::pair<const GRState*, const GRState *>(state, state); + + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty); + return state->assume(svalBuilder.evalEQ(state, *val, zero)); +} + +const GRState *CStringChecker::checkNonNull(CheckerContext &C, + const GRState *state, + const Expr *S, SVal l) { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + const GRState *stateNull, *stateNonNull; + llvm::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType()); + + if (stateNull && !stateNonNull) { + ExplodedNode *N = C.generateSink(stateNull); + if (!N) + return NULL; + + if (!BT_Null) + BT_Null = new BuiltinBug("API", + "Null pointer argument in call to byte string function"); + + // Generate a report for this bug. + BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null); + EnhancedBugReport *report = new EnhancedBugReport(*BT, + BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, S); + C.EmitReport(report); + return NULL; + } + + // From here on, assume that the value is non-null. + assert(stateNonNull); + return stateNonNull; +} + +// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? +const GRState *CStringChecker::CheckLocation(CheckerContext &C, + const GRState *state, + const Expr *S, SVal l, + bool IsDestination) { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return state; + + const ElementRegion *ER = dyn_cast<ElementRegion>(R); + if (!ER) + return state; + + assert(ER->getValueType() == C.getASTContext().CharTy && + "CheckLocation should only be called with char* ElementRegions"); + + // Get the size of the array. + const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal Extent = svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + DefinedOrUnknownSVal Size = cast<DefinedOrUnknownSVal>(Extent); + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + + const GRState *StInBound = state->assumeInBound(Idx, Size, true); + const GRState *StOutBound = state->assumeInBound(Idx, Size, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(StOutBound); + if (!N) + return NULL; + + BuiltinBug *BT; + if (IsDestination) { + if (!BT_BoundsWrite) { + BT_BoundsWrite = new BuiltinBug("Out-of-bound array access", + "Byte string function overflows destination buffer"); + } + BT = static_cast<BuiltinBug*>(BT_BoundsWrite); + } else { + if (!BT_Bounds) { + BT_Bounds = new BuiltinBug("Out-of-bound array access", + "Byte string function accesses out-of-bound array element"); + } + BT = static_cast<BuiltinBug*>(BT_Bounds); + } + + // 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. + RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + C.EmitReport(report); + return NULL; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + return StInBound; +} + +const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, + const GRState *state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf, + bool FirstIsDestination) { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = C.getASTContext(); + + QualType sizeTy = Size->getType(); + QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + + // Check that the first buffer is non-null. + SVal BufVal = state->getSVal(FirstBuf); + state = checkNonNull(C, state, FirstBuf, BufVal); + if (!state) + return NULL; + + // Get the access length and make sure it is known. + SVal LengthVal = state->getSVal(Size); + NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); + if (!Length) + return state; + + // Compute the offset of the last element to be accessed: size-1. + NonLoc One = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy)); + NonLoc LastOffset = cast<NonLoc>(svalBuilder.evalBinOpNN(state, BO_Sub, + *Length, One, sizeTy)); + + // Check that the first buffer is sufficently long. + SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); + if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, FirstBuf, BufEnd, FirstIsDestination); + + // If the buffer isn't large enough, abort. + if (!state) + return NULL; + } + + // If there's a second buffer, check it as well. + if (SecondBuf) { + BufVal = state->getSVal(SecondBuf); + state = checkNonNull(C, state, SecondBuf, BufVal); + if (!state) + return NULL; + + BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType()); + if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, SecondBuf, BufEnd); + } + } + + // Large enough or not, return this state! + return state; +} + +const GRState *CStringChecker::CheckOverlap(CheckerContext &C, + const GRState *state, + const Expr *Size, + const Expr *First, + const Expr *Second) { + // Do a simple check for overlap: if the two arguments are from the same + // buffer, see if the end of the first is greater than the start of the second + // or vice versa. + + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + const GRState *stateTrue, *stateFalse; + + // Get the buffer values and make sure they're known locations. + SVal firstVal = state->getSVal(First); + SVal secondVal = state->getSVal(Second); + + Loc *firstLoc = dyn_cast<Loc>(&firstVal); + if (!firstLoc) + return state; + + Loc *secondLoc = dyn_cast<Loc>(&secondVal); + if (!secondLoc) + return state; + + // Are the two values the same? + SValBuilder &svalBuilder = C.getSValBuilder(); + llvm::tie(stateTrue, stateFalse) = + state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc)); + + if (stateTrue && !stateFalse) { + // If the values are known to be equal, that's automatically an overlap. + emitOverlapBug(C, stateTrue, First, Second); + return NULL; + } + + // assume the two expressions are not equal. + assert(stateFalse); + state = stateFalse; + + // Which value comes first? + ASTContext &Ctx = svalBuilder.getContext(); + QualType cmpTy = Ctx.IntTy; + SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, + *firstLoc, *secondLoc, cmpTy); + DefinedOrUnknownSVal *reverseTest = dyn_cast<DefinedOrUnknownSVal>(&reverse); + if (!reverseTest) + return state; + + llvm::tie(stateTrue, stateFalse) = state->assume(*reverseTest); + if (stateTrue) { + if (stateFalse) { + // If we don't know which one comes first, we can't perform this test. + return state; + } else { + // Switch the values so that firstVal is before secondVal. + Loc *tmpLoc = firstLoc; + firstLoc = secondLoc; + secondLoc = tmpLoc; + + // Switch the Exprs as well, so that they still correspond. + const Expr *tmpExpr = First; + First = Second; + Second = tmpExpr; + } + } + + // Get the length, and make sure it too is known. + SVal LengthVal = state->getSVal(Size); + NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); + if (!Length) + return state; + + // Convert the first buffer's start address to char*. + // Bail out if the cast fails. + QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First->getType()); + Loc *FirstStartLoc = dyn_cast<Loc>(&FirstStart); + if (!FirstStartLoc) + return state; + + // Compute the end of the first buffer. Bail out if THAT fails. + SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, + *FirstStartLoc, *Length, CharPtrTy); + Loc *FirstEndLoc = dyn_cast<Loc>(&FirstEnd); + if (!FirstEndLoc) + return state; + + // Is the end of the first buffer past the start of the second buffer? + SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT, + *FirstEndLoc, *secondLoc, cmpTy); + DefinedOrUnknownSVal *OverlapTest = dyn_cast<DefinedOrUnknownSVal>(&Overlap); + if (!OverlapTest) + return state; + + llvm::tie(stateTrue, stateFalse) = state->assume(*OverlapTest); + + if (stateTrue && !stateFalse) { + // Overlap! + emitOverlapBug(C, stateTrue, First, Second); + return NULL; + } + + // assume the two expressions don't overlap. + assert(stateFalse); + return stateFalse; +} + +void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, + const Stmt *First, const Stmt *Second) { + ExplodedNode *N = C.generateSink(state); + if (!N) + return; + + if (!BT_Overlap) + BT_Overlap = new BugType("Unix API", "Improper arguments"); + + // Generate a report for this bug. + RangedBugReport *report = + new RangedBugReport(*BT_Overlap, + "Arguments must not be overlapping buffers", N); + report->addRange(First->getSourceRange()); + report->addRange(Second->getSourceRange()); + + C.EmitReport(report); +} + +const GRState *CStringChecker::setCStringLength(const GRState *state, + const MemRegion *MR, + SVal strLength) { + assert(!strLength.isUndef() && "Attempt to set an undefined string length"); + if (strLength.isUnknown()) + return state; + + MR = MR->StripCasts(); + + switch (MR->getKind()) { + case MemRegion::StringRegionKind: + // FIXME: This can happen if we strcpy() into a string region. This is + // undefined [C99 6.4.5p6], but we should still warn about it. + return state; + + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + return state->set<CStringLength>(MR, strLength); + + case MemRegion::ElementRegionKind: + // FIXME: Handle element regions by upper-bounding the parent region's + // string length. + return state; + + default: + // Other regions (mostly non-data) can't have a reliable C string length. + // For now, just ignore the change. + // FIXME: These are rare but not impossible. We should output some kind of + // warning for things like strcpy((char[]){'a', 0}, "b"); + return state; + } +} + +SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, + const GRState *&state, + const Expr *Ex, + const MemRegion *MR) { + // If there's a recorded length, go ahead and return it. + const SVal *Recorded = state->get<CStringLength>(MR); + if (Recorded) + return *Recorded; + + // Otherwise, get a new symbol and update the state. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + SVal strLength = svalBuilder.getMetadataSymbolVal(getTag(), MR, Ex, sizeTy, Count); + state = state->set<CStringLength>(MR, strLength); + return strLength; +} + +SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, + const Expr *Ex, SVal Buf) { + const MemRegion *MR = Buf.getAsRegion(); + if (!MR) { + // If we can't get a region, see if it's something we /know/ isn't a + // C string. In the context of locations, the only time we can issue such + // a warning is for labels. + if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&Buf)) { + if (ExplodedNode *N = C.generateNode(state)) { + if (!BT_NotCString) + BT_NotCString = new BuiltinBug("API", + "Argument is not a null-terminated string."); + + llvm::SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + os << "Argument to byte string function is the address of the label '" + << Label->getLabel()->getName() + << "', which is not a null-terminated string"; + + // Generate a report for this bug. + EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + os.str(), N); + + report->addRange(Ex->getSourceRange()); + C.EmitReport(report); + } + + return UndefinedVal(); + } + + // If it's not a region and not a label, give up. + return UnknownVal(); + } + + // If we have a region, strip casts from it and see if we can figure out + // its length. For anything we can't figure out, just return UnknownVal. + MR = MR->StripCasts(); + + switch (MR->getKind()) { + case MemRegion::StringRegionKind: { + // Modifying the contents of string regions is undefined [C99 6.4.5p6], + // so we can assume that the byte length is the correct C string length. + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral(); + return svalBuilder.makeIntVal(strLit->getByteLength(), sizeTy); + } + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + return getCStringLengthForRegion(C, state, Ex, MR); + case MemRegion::CompoundLiteralRegionKind: + // FIXME: Can we track this? Is it necessary? + return UnknownVal(); + case MemRegion::ElementRegionKind: + // FIXME: How can we handle this? It's not good enough to subtract the + // offset from the base string length; consider "123\x00567" and &a[5]. + return UnknownVal(); + default: + // Other regions (mostly non-data) can't have a reliable C string length. + // In this case, an error is emitted and UndefinedVal is returned. + // The caller should always be prepared to handle this case. + if (ExplodedNode *N = C.generateNode(state)) { + if (!BT_NotCString) + BT_NotCString = new BuiltinBug("API", + "Argument is not a null-terminated string."); + + llvm::SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + + os << "Argument to byte string function is "; + + if (SummarizeRegion(os, C.getASTContext(), MR)) + os << ", which is not a null-terminated string"; + else + os << "not a null-terminated string"; + + // Generate a report for this bug. + EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + os.str(), N); + + report->addRange(Ex->getSourceRange()); + C.EmitReport(report); + } + + return UndefinedVal(); + } +} + +const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C, + const GRState *state, + const Expr *E, SVal V) { + Loc *L = dyn_cast<Loc>(&V); + if (!L) + return state; + + // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes + // some assumptions about the value that CFRefCount can't. Even so, it should + // probably be refactored. + if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(L)) { + const MemRegion *R = MR->getRegion()->StripCasts(); + + // Are we dealing with an ElementRegion? If so, we should be invalidating + // the super-region. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + R = ER->getSuperRegion(); + // FIXME: What about layers of ElementRegions? + } + + // Invalidate this region. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + return state->invalidateRegion(R, E, Count, NULL); + } + + // If we have a non-region value by chance, just remove the binding. + // FIXME: is this necessary or correct? This handles the non-Region + // cases. Is it ever valid to store to these? + return state->unbindLoc(*L); +} + +bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR) { + const TypedRegion *TR = dyn_cast<TypedRegion>(MR); + if (!TR) + return false; + + switch (TR->getKind()) { + case MemRegion::FunctionTextRegionKind: { + const FunctionDecl *FD = cast<FunctionTextRegion>(TR)->getDecl(); + if (FD) + os << "the address of the function '" << FD << "'"; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockTextRegionKind: + os << "block text"; + return true; + case MemRegion::BlockDataRegionKind: + os << "a block"; + return true; + case MemRegion::CXXThisRegionKind: + case MemRegion::CXXTempObjectRegionKind: + os << "a C++ temp object of type " << TR->getValueType().getAsString(); + return true; + case MemRegion::VarRegionKind: + os << "a variable of type" << TR->getValueType().getAsString(); + return true; + case MemRegion::FieldRegionKind: + os << "a field of type " << TR->getValueType().getAsString(); + return true; + case MemRegion::ObjCIvarRegionKind: + os << "an instance variable of type " << TR->getValueType().getAsString(); + return true; + default: + return false; + } +} + +//===----------------------------------------------------------------------===// +// evaluation of individual function calls. +//===----------------------------------------------------------------------===// + +void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *Dest, + const Expr *Source, bool Restricted) { + // See if the size argument is zero. + SVal sizeVal = state->getSVal(Size); + QualType sizeTy = Size->getType(); + + const GRState *stateZeroSize, *stateNonZeroSize; + llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); + + // If the size is zero, there won't be any actual memory access. + if (stateZeroSize) + C.addTransition(stateZeroSize); + + // If the size can be nonzero, we have to check the other arguments. + if (stateNonZeroSize) { + state = stateNonZeroSize; + state = CheckBufferAccess(C, state, Size, Dest, Source, + /* FirstIsDst = */ true); + if (Restricted) + state = CheckOverlap(C, state, Size, Dest, Source); + + if (state) { + // Invalidate the destination. + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // copied region, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dest, state->getSVal(Dest)); + C.addTransition(state); + } + } +} + + +void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) { + // 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); + const GRState *state = C.getState(); + state = state->BindExpr(CE, state->getSVal(Dest)); + evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1), true); +} + +void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) { + // 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); + const GRState *state = C.getState(); + state = state->BindExpr(CE, state->getSVal(Dest)); + evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1)); +} + +void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) { + // void bcopy(const void *src, void *dst, size_t n); + evalCopyCommon(C, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0)); +} + +void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) { + // int memcmp(const void *s1, const void *s2, size_t n); + const Expr *Left = CE->getArg(0); + const Expr *Right = CE->getArg(1); + const Expr *Size = CE->getArg(2); + + const GRState *state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + // See if the size argument is zero. + SVal sizeVal = state->getSVal(Size); + QualType sizeTy = Size->getType(); + + const GRState *stateZeroSize, *stateNonZeroSize; + llvm::tie(stateZeroSize, stateNonZeroSize) = + assumeZero(C, state, sizeVal, sizeTy); + + // If the size can be zero, the result will be 0 in that case, and we don't + // have to check either of the buffers. + if (stateZeroSize) { + state = stateZeroSize; + state = state->BindExpr(CE, svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(state); + } + + // If the size can be nonzero, we have to check the other arguments. + if (stateNonZeroSize) { + state = stateNonZeroSize; + // If we know the two buffers are the same, we know the result is 0. + // First, get the two buffers' addresses. Another checker will have already + // made sure they're not undefined. + DefinedOrUnknownSVal LV = cast<DefinedOrUnknownSVal>(state->getSVal(Left)); + DefinedOrUnknownSVal RV = cast<DefinedOrUnknownSVal>(state->getSVal(Right)); + + // See if they are the same. + DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); + const GRState *StSameBuf, *StNotSameBuf; + llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); + + // If the two arguments might be the same buffer, we know the result is zero, + // and we only need to check one size. + if (StSameBuf) { + state = StSameBuf; + state = CheckBufferAccess(C, state, Size, Left); + if (state) { + state = StSameBuf->BindExpr(CE, svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(state); + } + } + + // If the two arguments might be different buffers, we have to check the + // size of both of them. + if (StNotSameBuf) { + state = StNotSameBuf; + state = CheckBufferAccess(C, state, Size, Left, Right); + if (state) { + // The return value is the comparison result, which we don't know. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SVal CmpV = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + state = state->BindExpr(CE, CmpV); + C.addTransition(state); + } + } + } +} + +void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) { + // size_t strlen(const char *s); + const GRState *state = C.getState(); + const Expr *Arg = CE->getArg(0); + SVal ArgVal = state->getSVal(Arg); + + // Check that the argument is non-null. + state = checkNonNull(C, state, Arg, ArgVal); + + if (state) { + SVal strLength = getCStringLength(C, state, Arg, ArgVal); + + // If the argument isn't a valid C string, there's no valid state to + // transition to. + if (strLength.isUndef()) + return; + + // If getCStringLength couldn't figure out the length, conjure a return + // value, so it can be used in constraints, at least. + if (strLength.isUnknown()) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + strLength = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + } + + // Bind the return value. + state = state->BindExpr(CE, strLength); + C.addTransition(state); + } +} + +void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) { + // char *strcpy(char *restrict dst, const char *restrict src); + evalStrcpyCommon(C, CE, /* returnEnd = */ false); +} + +void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) { + // char *stpcpy(char *restrict dst, const char *restrict src); + evalStrcpyCommon(C, CE, /* returnEnd = */ true); +} + +void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, + bool returnEnd) { + const GRState *state = C.getState(); + + // Check that the destination is non-null + const Expr *Dst = CE->getArg(0); + SVal DstVal = state->getSVal(Dst); + + state = checkNonNull(C, state, Dst, DstVal); + if (!state) + return; + + // Check that the source is non-null. + const Expr *srcExpr = CE->getArg(1); + SVal srcVal = state->getSVal(srcExpr); + state = checkNonNull(C, state, srcExpr, srcVal); + if (!state) + return; + + // Get the string length of the source. + SVal strLength = getCStringLength(C, state, srcExpr, srcVal); + + // If the source isn't a valid C string, give up. + if (strLength.isUndef()) + return; + + SVal Result = (returnEnd ? UnknownVal() : DstVal); + + // If the destination is a MemRegion, try to check for a buffer overflow and + // record the new string length. + if (loc::MemRegionVal *dstRegVal = dyn_cast<loc::MemRegionVal>(&DstVal)) { + // If the length is known, we can check for an overflow. + if (NonLoc *knownStrLength = dyn_cast<NonLoc>(&strLength)) { + SVal lastElement = + C.getSValBuilder().evalBinOpLN(state, BO_Add, *dstRegVal, + *knownStrLength, Dst->getType()); + + state = CheckLocation(C, state, Dst, lastElement, /* IsDst = */ true); + if (!state) + return; + + // If this is a stpcpy-style copy, the last element is the return value. + if (returnEnd) + Result = lastElement; + } + + // Invalidate the destination. This must happen before we set the C string + // length because invalidation will clear the length. + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // string, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dst, *dstRegVal); + + // Set the C string length of the destination. + state = setCStringLength(state, dstRegVal->getRegion(), strLength); + } + + // If this is a stpcpy-style copy, but we were unable to check for a buffer + // overflow, we still need a result. Conjure a return value. + if (returnEnd && Result.isUnknown()) { + SValBuilder &svalBuilder = C.getSValBuilder(); + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + strLength = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + } + + // Set the return value. + state = state->BindExpr(CE, Result); + C.addTransition(state); +} + +//===----------------------------------------------------------------------===// +// The driver method, and other Checker callbacks. +//===----------------------------------------------------------------------===// + +bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + // Get the callee. All the functions we care about are C functions + // with simple identifiers. + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl(); + + if (!FD) + return false; + + // Get the name of the callee. If it's a builtin, strip off the prefix. + IdentifierInfo *II = FD->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return false; + llvm::StringRef Name = II->getName(); + if (Name.startswith("__builtin_")) + Name = Name.substr(10); + + FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) + .Cases("memcpy", "__memcpy_chk", &CStringChecker::evalMemcpy) + .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp) + .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove) + .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) + .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) + .Case("strlen", &CStringChecker::evalstrLength) + .Case("bcopy", &CStringChecker::evalBcopy) + .Default(NULL); + + // If the callee isn't a string function, let another checker handle it. + if (!evalFunction) + return false; + + // Check and evaluate the call. + (this->*evalFunction)(C, CE); + return true; +} + +void CStringChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + // Record string length for char a[] = "abc"; + const GRState *state = C.getState(); + + for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end(); + I != E; ++I) { + const VarDecl *D = dyn_cast<VarDecl>(*I); + if (!D) + continue; + + // FIXME: Handle array fields of structs. + if (!D->getType()->isArrayType()) + continue; + + const Expr *Init = D->getInit(); + if (!Init) + continue; + if (!isa<StringLiteral>(Init)) + continue; + + Loc VarLoc = state->getLValue(D, C.getPredecessor()->getLocationContext()); + const MemRegion *MR = VarLoc.getAsRegion(); + if (!MR) + continue; + + SVal StrVal = state->getSVal(Init); + assert(StrVal.isValid() && "Initializer string is unknown or undefined"); + DefinedOrUnknownSVal strLength + = cast<DefinedOrUnknownSVal>(getCStringLength(C, state, Init, StrVal)); + + state = state->set<CStringLength>(MR, strLength); + } + + C.addTransition(state); +} + +bool CStringChecker::wantsRegionChangeUpdate(const GRState *state) { + CStringLength::EntryMap Entries = state->get<CStringLength>(); + return !Entries.isEmpty(); +} + +const GRState *CStringChecker::EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool *) { + CStringLength::EntryMap Entries = state->get<CStringLength>(); + if (Entries.isEmpty()) + return state; + + llvm::SmallPtrSet<const MemRegion *, 8> Invalidated; + llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions; + + // First build sets for the changed regions and their super-regions. + for ( ; Begin != End; ++Begin) { + const MemRegion *MR = *Begin; + Invalidated.insert(MR); + + SuperRegions.insert(MR); + while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) { + MR = SR->getSuperRegion(); + SuperRegions.insert(MR); + } + } + + CStringLength::EntryMap::Factory &F = state->get_context<CStringLength>(); + + // Then loop over the entries in the current state. + for (CStringLength::EntryMap::iterator I = Entries.begin(), + E = Entries.end(); I != E; ++I) { + const MemRegion *MR = I.getKey(); + + // Is this entry for a super-region of a changed region? + if (SuperRegions.count(MR)) { + Entries = F.remove(Entries, MR); + continue; + } + + // Is this entry for a sub-region of a changed region? + const MemRegion *Super = MR; + while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) { + Super = SR->getSuperRegion(); + if (Invalidated.count(Super)) { + Entries = F.remove(Entries, MR); + break; + } + } + } + + return state->set<CStringLength>(Entries); +} + +void CStringChecker::MarkLiveSymbols(const GRState *state, SymbolReaper &SR) { + // Mark all symbols in our string length map as valid. + CStringLength::EntryMap Entries = state->get<CStringLength>(); + + for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + if (SymbolRef Sym = Len.getAsSymbol()) + SR.markInUse(Sym); + } +} + +void CStringChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SR) { + if (!SR.hasDeadSymbols()) + return; + + const GRState *state = C.getState(); + CStringLength::EntryMap Entries = state->get<CStringLength>(); + if (Entries.isEmpty()) + return; + + CStringLength::EntryMap::Factory &F = state->get_context<CStringLength>(); + for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + if (SymbolRef Sym = Len.getAsSymbol()) { + if (SR.isDead(Sym)) + Entries = F.remove(Entries, I.getKey()); + } + } + + state = state->set<CStringLength>(Entries); + C.generateNode(state); +} diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp new file mode 100644 index 0000000..415900e --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -0,0 +1,358 @@ +//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CallAndMessageChecker, a builtin checker that checks for various +// errors of call and objc message expressions. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class CallAndMessageChecker + : public CheckerVisitor<CallAndMessageChecker> { + BugType *BT_call_null; + BugType *BT_call_undef; + BugType *BT_call_arg; + BugType *BT_msg_undef; + BugType *BT_msg_arg; + BugType *BT_msg_ret; +public: + CallAndMessageChecker() : + BT_call_null(0), BT_call_undef(0), BT_call_arg(0), + BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); + bool evalNilReceiver(CheckerContext &C, ObjCMessage msg); + +private: + void PreVisitProcessArgs(CheckerContext &C, CallOrObjCMessage callOrMsg, + const char *BT_desc, BugType *&BT); + bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, + const Expr *argEx, const char *BT_desc, BugType *&BT); + + void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); + void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, + ExplodedNode *N); + + void HandleNilReceiver(CheckerContext &C, const GRState *state, + ObjCMessage msg); + + void LazyInit_BT(const char *desc, BugType *&BT) { + if (!BT) + BT = new BuiltinBug(desc); + } +}; +} // end anonymous namespace + +void ento::RegisterCallAndMessageChecker(ExprEngine &Eng) { + Eng.registerCheck(new CallAndMessageChecker()); +} + +void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, + const CallExpr *CE) { + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetCalleeExpr(N)); + C.EmitReport(R); +} + +void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C, + CallOrObjCMessage callOrMsg, + const char *BT_desc, + BugType *&BT) { + for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i) + if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i), + callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i), + BT_desc, BT)) + return; +} + +bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, + SVal V, SourceRange argRange, + const Expr *argEx, + const char *BT_desc, + BugType *&BT) { + + if (V.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + LazyInit_BT(BT_desc, BT); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addRange(argRange); + if (argEx) + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, argEx); + C.EmitReport(R); + } + return true; + } + + if (const nonloc::LazyCompoundVal *LV = + dyn_cast<nonloc::LazyCompoundVal>(&V)) { + + class FindUninitializedField { + public: + llvm::SmallVector<const FieldDecl *, 10> FieldChain; + private: + ASTContext &C; + StoreManager &StoreMgr; + MemRegionManager &MrMgr; + Store store; + public: + FindUninitializedField(ASTContext &c, StoreManager &storeMgr, + MemRegionManager &mrMgr, Store s) + : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} + + bool Find(const TypedRegion *R) { + QualType T = R->getValueType(); + if (const RecordType *RT = T->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + assert(RD && "Referred record has no definition"); + for (RecordDecl::field_iterator I = + RD->field_begin(), E = RD->field_end(); I!=E; ++I) { + const FieldRegion *FR = MrMgr.getFieldRegion(*I, R); + FieldChain.push_back(*I); + T = (*I)->getType(); + if (T->getAsStructureType()) { + if (Find(FR)) + return true; + } + else { + const SVal &V = StoreMgr.Retrieve(store, loc::MemRegionVal(FR)); + if (V.isUndef()) + return true; + } + FieldChain.pop_back(); + } + } + + return false; + } + }; + + const LazyCompoundValData *D = LV->getCVData(); + FindUninitializedField F(C.getASTContext(), + C.getState()->getStateManager().getStoreManager(), + C.getSValBuilder().getRegionManager(), + D->getStore()); + + if (F.Find(D->getRegion())) { + if (ExplodedNode *N = C.generateSink()) { + LazyInit_BT(BT_desc, BT); + llvm::SmallString<512> Str; + llvm::raw_svector_ostream os(Str); + os << "Passed-by-value struct argument contains uninitialized data"; + + if (F.FieldChain.size() == 1) + os << " (e.g., field: '" << F.FieldChain[0] << "')"; + else { + os << " (e.g., via the field chain: '"; + bool first = true; + for (llvm::SmallVectorImpl<const FieldDecl *>::iterator + DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ + if (first) + first = false; + else + os << '.'; + os << *DI; + } + os << "')"; + } + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + R->addRange(argRange); + + // FIXME: enhance track back for uninitialized value for arbitrary + // memregions + C.EmitReport(R); + } + return true; + } + } + + return false; +} + +void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE){ + + const Expr *Callee = CE->getCallee()->IgnoreParens(); + SVal L = C.getState()->getSVal(Callee); + + if (L.isUndef()) { + if (!BT_call_undef) + BT_call_undef = + new BuiltinBug("Called function pointer is an uninitalized pointer value"); + EmitBadCall(BT_call_undef, C, CE); + return; + } + + if (isa<loc::ConcreteInt>(L)) { + if (!BT_call_null) + BT_call_null = + new BuiltinBug("Called function pointer is null (null dereference)"); + EmitBadCall(BT_call_null, C, CE); + } + + PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState()), + "Function call argument is an uninitialized value", + BT_call_arg); +} + +void CallAndMessageChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { + + const GRState *state = C.getState(); + + // FIXME: Handle 'super'? + if (const Expr *receiver = msg.getInstanceReceiver()) + if (state->getSVal(receiver).isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_msg_undef) + BT_msg_undef = + new BuiltinBug("Receiver in message expression is an uninitialized value"); + EnhancedBugReport *R = + new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); + R->addRange(receiver->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + C.EmitReport(R); + } + return; + } + + const char *bugDesc = msg.isPropertySetter() ? + "Argument for property setter is an uninitialized value" + : "Argument in message expression is an uninitialized value"; + // Check for any arguments that are uninitialized/undefined. + PreVisitProcessArgs(C, CallOrObjCMessage(msg, state), bugDesc, BT_msg_arg); +} + +bool CallAndMessageChecker::evalNilReceiver(CheckerContext &C, + ObjCMessage msg) { + HandleNilReceiver(C, C.getState(), msg); + return true; // Nil receiver is not handled elsewhere. +} + +void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, + const ObjCMessage &msg, + ExplodedNode *N) { + + if (!BT_msg_ret) + BT_msg_ret = + new BuiltinBug("Receiver in message expression is " + "'nil' and returns a garbage value"); + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "The receiver of message '" << msg.getSelector().getAsString() + << "' is nil and returns a value of type '" + << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); + if (const Expr *receiver = msg.getInstanceReceiver()) { + report->addRange(receiver->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + } + C.EmitReport(report); +} + +static bool supportsNilWithFloatRet(const llvm::Triple &triple) { + return triple.getVendor() == llvm::Triple::Apple && + (triple.getDarwinMajorNumber() >= 9 || + triple.getArch() == llvm::Triple::arm || + triple.getArch() == llvm::Triple::thumb); +} + +void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, + const GRState *state, + ObjCMessage msg) { + ASTContext &Ctx = C.getASTContext(); + + // Check the return type of the message expression. A message to nil will + // return different values depending on the return type and the architecture. + QualType RetTy = msg.getType(Ctx); + + CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); + + if (CanRetTy->isStructureOrClassType()) { + // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead + // have the "use of undefined value" be smarter about where the + // undefined value came from. + if (C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())){ + if (ExplodedNode* N = C.generateSink(state)) + emitNilReceiverBug(C, msg, N); + return; + } + + // The result is not consumed by a surrounding expression. Just propagate + // the current state. + C.addTransition(state); + return; + } + + // Other cases: check if the return type is smaller than void*. + if (CanRetTy != Ctx.VoidTy && + C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())) { + // Compute: sizeof(void *) and sizeof(return type) + const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); + const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); + + if (voidPtrSize < returnTypeSize && + !(supportsNilWithFloatRet(Ctx.Target.getTriple()) && + (Ctx.FloatTy == CanRetTy || + Ctx.DoubleTy == CanRetTy || + Ctx.LongDoubleTy == CanRetTy || + Ctx.LongLongTy == CanRetTy || + Ctx.UnsignedLongLongTy == CanRetTy))) { + if (ExplodedNode* N = C.generateSink(state)) + emitNilReceiverBug(C, msg, N); + return; + } + + // Handle the safe cases where the return value is 0 if the + // receiver is nil. + // + // FIXME: For now take the conservative approach that we only + // return null values if we *know* that the receiver is nil. + // This is because we can have surprises like: + // + // ... = [[NSScreens screens] objectAtIndex:0]; + // + // What can happen is that [... screens] could return nil, but + // it most likely isn't nil. We should assume the semantics + // of this case unless we have *a lot* more knowledge. + // + SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); + C.generateNode(state->BindExpr(msg.getOriginExpr(), V)); + return; + } + + C.addTransition(state); +} diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp new file mode 100644 index 0000000..518cf96 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -0,0 +1,91 @@ +//=== 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 "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "InternalChecks.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastSizeChecker : public CheckerVisitor<CastSizeChecker> { + BuiltinBug *BT; +public: + CastSizeChecker() : BT(0) {} + static void *getTag(); + void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); +}; +} + +void *CastSizeChecker::getTag() { + static int x; + return &x; +} + +void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { + 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; + + const GRState *state = C.getState(); + const MemRegion *R = state->getSVal(E).getAsRegion(); + if (R == 0) + return; + + const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); + if (SR == 0) + 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) { + if (ExplodedNode *errorNode = C.generateSink()) { + if (!BT) + BT = new BuiltinBug("Cast region with wrong size.", + "Cast a region whose size is not a multiple of the" + " destination type size."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), + errorNode); + R->addRange(CE->getSourceRange()); + C.EmitReport(R); + } + } +} + + +void ento::RegisterCastSizeChecker(ExprEngine &Eng) { + Eng.registerCheck(new CastSizeChecker()); +} diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp new file mode 100644 index 0000000..8ec226a --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -0,0 +1,84 @@ +//=== CastToStructChecker.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. +// +//===----------------------------------------------------------------------===// +// +// This files defines CastToStructChecker, a builtin checker that checks for +// cast from non-struct pointer to struct pointer. +// This check corresponds to CWE-588. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastToStructChecker + : public CheckerVisitor<CastToStructChecker> { + BuiltinBug *BT; +public: + CastToStructChecker() : BT(0) {} + static void *getTag(); + void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); +}; +} + +void *CastToStructChecker::getTag() { + static int x; + return &x; +} + +void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, + const CastExpr *CE) { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = C.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; + + QualType OrigPointeeTy = OrigPTy->getPointeeType(); + QualType ToPointeeTy = ToPTy->getPointeeType(); + + if (!ToPointeeTy->isStructureOrClassType()) + return; + + // We allow cast from void*. + if (OrigPointeeTy->isVoidType()) + return; + + // Now the cast-to-type is struct pointer, the original type is not void*. + if (!OrigPointeeTy->isRecordType()) { + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Cast from non-struct type to struct type", + "Casting a non-structure type to a structure type " + "and accessing a field can lead to memory access " + "errors or data corruption."); + RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); + R->addRange(CE->getSourceRange()); + C.EmitReport(R); + } + } +} + +static void RegisterCastToStructChecker(ExprEngine &Eng) { + Eng.registerCheck(new CastToStructChecker()); +} + +void ento::registerCastToStructChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterCastToStructChecker); +} diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp new file mode 100644 index 0000000..ad3bab6 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -0,0 +1,284 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCDealloc, a checker that +// analyzes an Objective-C class's implementation to determine if it +// correctly implements -dealloc. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool scan_dealloc(Stmt* S, Selector Dealloc) { + + if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) + if (ME->getSelector() == Dealloc) { + switch (ME->getReceiverKind()) { + case ObjCMessageExpr::Instance: return false; + case ObjCMessageExpr::SuperInstance: return true; + case ObjCMessageExpr::Class: break; + case ObjCMessageExpr::SuperClass: break; + } + } + + // Recurse to children. + + for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) + if (*I && scan_dealloc(*I, Dealloc)) + return true; + + return false; +} + +static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, + const ObjCPropertyDecl* PD, + Selector Release, + IdentifierInfo* SelfII, + ASTContext& Ctx) { + + // [mMyIvar release] + if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) + if (ME->getSelector() == Release) + if (ME->getInstanceReceiver()) + if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) + if (ObjCIvarRefExpr* E = dyn_cast<ObjCIvarRefExpr>(Receiver)) + if (E->getDecl() == ID) + return true; + + // [self setMyIvar:nil]; + if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) + if (ME->getInstanceReceiver()) + if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) + if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(Receiver)) + if (E->getDecl()->getIdentifier() == SelfII) + if (ME->getMethodDecl() == PD->getSetterMethodDecl() && + ME->getNumArgs() == 1 && + ME->getArg(0)->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return true; + + // self.myIvar = nil; + if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S)) + if (BO->isAssignmentOp()) + if (ObjCPropertyRefExpr* PRE = + dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts())) + if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) + if (BO->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) { + // This is only a 'release' if the property kind is not + // 'assign'. + return PD->getSetterKind() != ObjCPropertyDecl::Assign;; + } + + // Recurse to children. + for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) + if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx)) + return true; + + return false; +} + +static void checkObjCDealloc(const ObjCImplementationDecl* D, + const LangOptions& LOpts, BugReporter& BR) { + + assert (LOpts.getGCMode() != LangOptions::GCOnly); + + ASTContext& Ctx = BR.getContext(); + const ObjCInterfaceDecl* ID = D->getClassInterface(); + + // Does the class contain any ivars that are pointers (or id<...>)? + // If not, skip the check entirely. + // NOTE: This is motivated by PR 2517: + // http://llvm.org/bugs/show_bug.cgi?id=2517 + + bool containsPointerIvar = false; + + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); + I!=E; ++I) { + + ObjCIvarDecl* ID = *I; + QualType T = ID->getType(); + + if (!T->isObjCObjectPointerType() || + ID->getAttr<IBOutletAttr>() || // Skip IBOutlets. + ID->getAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections. + continue; + + containsPointerIvar = true; + break; + } + + if (!containsPointerIvar) + return; + + // Determine if the class subclasses NSObject. + IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); + IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + + + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + break; + + // FIXME: For now, ignore classes that subclass SenTestCase, as these don't + // need to implement -dealloc. They implement tear down in another way, + // which we should try and catch later. + // http://llvm.org/bugs/show_bug.cgi?id=3187 + if (II == SenTestCaseII) + return; + } + + if (!ID) + return; + + // Get the "dealloc" selector. + IdentifierInfo* II = &Ctx.Idents.get("dealloc"); + Selector S = Ctx.Selectors.getSelector(0, &II); + ObjCMethodDecl* MD = 0; + + // Scan the instance methods for "dealloc". + for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) { + + if ((*I)->getSelector() == S) { + MD = *I; + break; + } + } + + if (!MD) { // No dealloc found. + + const char* name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing -dealloc" + : "missing -dealloc (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method"; + + BR.EmitBasicReport(name, os.str(), D->getLocStart()); + return; + } + + // dealloc found. Scan for missing [super dealloc]. + if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { + + const char* name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing [super dealloc]" + : "missing [super dealloc] (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "The 'dealloc' instance method in Objective-C class '" << D + << "' does not send a 'dealloc' message to its super class" + " (missing [super dealloc])"; + + BR.EmitBasicReport(name, os.str(), D->getLocStart()); + return; + } + + // Get the "release" selector. + IdentifierInfo* RII = &Ctx.Idents.get("release"); + Selector RS = Ctx.Selectors.getSelector(0, &RII); + + // Get the "self" identifier + IdentifierInfo* SelfII = &Ctx.Idents.get("self"); + + // Scan for missing and extra releases of ivars used by implementations + // of synthesized properties + for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), + E = D->propimpl_end(); I!=E; ++I) { + + // We can only check the synthesized properties + if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + + ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl(); + if (!ID) + continue; + + QualType T = ID->getType(); + if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars + continue; + + const ObjCPropertyDecl* PD = (*I)->getPropertyDecl(); + if (!PD) + continue; + + // ivars cannot be set via read-only properties, so we'll skip them + if (PD->isReadOnly()) + continue; + + // ivar must be released if and only if the kind of setter was not 'assign' + bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; + if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) + != requiresRelease) { + const char *name; + const char* category = "Memory (Core Foundation/Objective-C)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + + if (requiresRelease) { + name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing ivar release (leak)" + : "missing ivar release (Hybrid MM, non-GC)"; + + os << "The '" << ID + << "' instance variable was retained by a synthesized property but " + "wasn't released in 'dealloc'"; + } else { + name = LOpts.getGCMode() == LangOptions::NonGC + ? "extra ivar release (use-after-release)" + : "extra ivar release (Hybrid MM, non-GC)"; + + os << "The '" << ID + << "' instance variable was not retained by a synthesized property " + "but was released in 'dealloc'"; + } + + BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); + } + } +} + +//===----------------------------------------------------------------------===// +// ObjCDeallocChecker +//===----------------------------------------------------------------------===// + +namespace { +class ObjCDeallocChecker : public CheckerV2< + check::ASTDecl<ObjCImplementationDecl> > { +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOptions(), BR); + } +}; +} + +void ento::registerObjCDeallocChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCDeallocChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp new file mode 100644 index 0000000..369ba0b --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -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/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "clang/AST/ASTContext.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) { + + QualType ResDerived = MethDerived->getResultType(); + QualType ResAncestor = MethAncestor->getResultType(); + + 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().getAsString() + << "' 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."; + + BR.EmitBasicReport("Incompatible instance method return type", + os.str(), MethDerived->getLocStart()); + } +} + +static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, + BugReporter& BR) { + + 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 (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), + E=ID->instmeth_end(); I!=E; ++I) { + + ObjCMethodDecl* M = *I; + IMeths[M->getSelector()] = M; + ++NumMethods; + } + + // Now recurse the class hierarchy chain looking for methods with the + // same signatures. + while (C && NumMethods) { + for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), + E=C->instmeth_end(); I!=E; ++I) { + + ObjCMethodDecl* M = *I; + Selector S = M->getSelector(); + + MapTy::iterator MI = IMeths.find(S); + + if (MI == IMeths.end() || MI->second == 0) + continue; + + --NumMethods; + ObjCMethodDecl* MethDerived = MI->second; + MI->second = 0; + + CompareReturnTypes(MethDerived, M, BR, Ctx, ID); + } + + C = C->getSuperClass(); + } +} + +//===----------------------------------------------------------------------===// +// ObjCMethSigsChecker +//===----------------------------------------------------------------------===// + +namespace { +class ObjCMethSigsChecker : public CheckerV2< + check::ASTDecl<ObjCImplementationDecl> > { +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, + BugReporter &BR) const { + CheckObjCInstMethSignature(D, BR); + } +}; +} + +void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCMethSigsChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp new file mode 100644 index 0000000..185520c --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -0,0 +1,517 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of flow-insensitive security checks. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool isArc4RandomAvailable(const ASTContext &Ctx) { + const llvm::Triple &T = Ctx.Target.getTriple(); + return T.getVendor() == llvm::Triple::Apple || + T.getOS() == llvm::Triple::FreeBSD || + T.getOS() == llvm::Triple::NetBSD || + T.getOS() == llvm::Triple::OpenBSD || + T.getOS() == llvm::Triple::DragonFly; +} + +namespace { +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + IdentifierInfo *II_gets; + IdentifierInfo *II_getpw; + IdentifierInfo *II_mktemp; + enum { num_rands = 9 }; + IdentifierInfo *II_rand[num_rands]; + IdentifierInfo *II_random; + enum { num_setids = 6 }; + IdentifierInfo *II_setid[num_setids]; + + const bool CheckRand; + +public: + WalkAST(BugReporter &br) : BR(br), + II_gets(0), II_getpw(0), II_mktemp(0), + II_rand(), II_random(0), II_setid(), + CheckRand(isArc4RandomAvailable(BR.getContext())) {} + + // Statement visitor methods. + void VisitCallExpr(CallExpr *CE); + void VisitForStmt(ForStmt *S); + void VisitCompoundStmt (CompoundStmt *S); + void VisitStmt(Stmt *S) { VisitChildren(S); } + + void VisitChildren(Stmt *S); + + // Helpers. + IdentifierInfo *GetIdentifier(IdentifierInfo *& II, const char *str); + + // Checker-specific methods. + void CheckLoopConditionForFloat(const ForStmt *FS); + void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_mktemp(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 CheckUncheckedReturnValue(CallExpr *CE); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Helper methods. +//===----------------------------------------------------------------------===// + +IdentifierInfo *WalkAST::GetIdentifier(IdentifierInfo *& II, const char *str) { + if (!II) + II = &BR.getContext().Idents.get(str); + + return II; +} + +//===----------------------------------------------------------------------===// +// AST walking. +//===----------------------------------------------------------------------===// + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + if (const FunctionDecl *FD = CE->getDirectCallee()) { + CheckCall_gets(CE, FD); + CheckCall_getpw(CE, FD); + CheckCall_mktemp(CE, FD); + if (CheckRand) { + CheckCall_rand(CE, FD); + CheckCall_random(CE, FD); + } + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitCompoundStmt(CompoundStmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) { + if (CallExpr *CE = dyn_cast<CallExpr>(child)) + CheckUncheckedReturnValue(CE); + Visit(child); + } +} + +void WalkAST::VisitForStmt(ForStmt *FS) { + CheckLoopConditionForFloat(FS); + + // Recurse and check children. + VisitChildren(FS); +} + +//===----------------------------------------------------------------------===// +// Check: floating poing variable used as loop counter. +// Originally: <rdar://problem/6336718> +// Implements: CERT security coding advisory FLP-30. +//===----------------------------------------------------------------------===// + +static const DeclRefExpr* +GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { + expr = expr->IgnoreParenCasts(); + + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { + if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || + B->getOpcode() == BO_Comma)) + return NULL; + + if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) + return lhs; + + if (const DeclRefExpr *rhs = GetIncrementedVar(B->getRHS(), x, y)) + return rhs; + + return NULL; + } + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { + const NamedDecl *ND = DR->getDecl(); + return ND == x || ND == y ? DR : NULL; + } + + if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) + return U->isIncrementDecrementOp() + ? GetIncrementedVar(U->getSubExpr(), x, y) : NULL; + + return NULL; +} + +/// CheckLoopConditionForFloat - This check looks for 'for' statements that +/// use a floating point variable as a loop counter. +/// CERT: FLP30-C, FLP30-CPP. +/// +void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { + // Does the loop have a condition? + const Expr *condition = FS->getCond(); + + if (!condition) + return; + + // Does the loop have an increment? + const Expr *increment = FS->getInc(); + + if (!increment) + return; + + // Strip away '()' and casts. + condition = condition->IgnoreParenCasts(); + increment = increment->IgnoreParenCasts(); + + // Is the loop condition a comparison? + const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); + + if (!B) + return; + + // Is this a comparison? + if (!(B->isRelationalOp() || B->isEqualityOp())) + return; + + // Are we comparing variables? + const DeclRefExpr *drLHS = + dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); + const DeclRefExpr *drRHS = + dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); + + // Does at least one of the variables have a floating point type? + drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : NULL; + drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : NULL; + + if (!drLHS && !drRHS) + return; + + const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : NULL; + const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : NULL; + + if (!vdLHS && !vdRHS) + return; + + // Does either variable appear in increment? + const DeclRefExpr *drInc = GetIncrementedVar(increment, vdLHS, vdRHS); + + if (!drInc) + return; + + // Emit the error. First figure out which DeclRefExpr in the condition + // referenced the compared variable. + const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; + + llvm::SmallVector<SourceRange, 2> ranges; + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + + os << "Variable '" << drCond->getDecl()->getName() + << "' with floating point type '" << drCond->getType().getAsString() + << "' should not be used as a loop counter"; + + ranges.push_back(drCond->getSourceRange()); + ranges.push_back(drInc->getSourceRange()); + + const char *bugType = "Floating point variable used as loop counter"; + BR.EmitBasicReport(bugType, "Security", os.str(), + FS->getLocStart(), ranges.data(), ranges.size()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'gets' is insecure. +// Originally: <rdar://problem/6335715> +// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) +// CWE-242: Use of Inherently Dangerous Function +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) + return; + + const FunctionProtoType *FPT + = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); + if (!FPT) + return; + + // Verify that the function takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Is the argument a 'char*'? + const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", + "Security", + "Call to function 'gets' is extremely insecure as it can " + "always result in a buffer overflow", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'getpwd' is insecure. +// CWE-477: Use of Obsolete Functions +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) + return; + + const FunctionProtoType *FPT + = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); + if (!FPT) + return; + + // Verify that the function takes two arguments. + if (FPT->getNumArgs() != 2) + return; + + // Verify the first argument type is integer. + if (!FPT->getArgType(0)->isIntegerType()) + return; + + // Verify the second argument type is char*. + const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(1)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", + "Security", + "The getpw() function is dangerous as it may overflow the " + "provided buffer. It is obsoleted by getpwuid().", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). +// CWE-377: Insecure Temporary File +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) + return; + + const FunctionProtoType *FPT + = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); + if(!FPT) + return; + + // Verify that the funcion takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Verify that the argument is Pointer Type. + const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); + if (!PT) + return; + + // Verify that the argument is a 'char*'. + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a waring. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", + "Security", + "Call to function 'mktemp' is insecure as it always " + "creates or uses insecure temporary file. Use 'mkstemp' instead", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Linear congruent random number generators should not be used +// Originally: <rdar://problem/63371000> +// CWE-338: Use of cryptographically weak prng +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { + if (II_rand[0] == NULL) { + // This check applies to these functions + static const char * const identifiers[num_rands] = { + "drand48", "erand48", "jrand48", "lrand48", "mrand48", "nrand48", + "lcong48", + "rand", "rand_r" + }; + + for (size_t i = 0; i < num_rands; i++) + II_rand[i] = &BR.getContext().Idents.get(identifiers[i]); + } + + const IdentifierInfo *id = FD->getIdentifier(); + size_t identifierid; + + for (identifierid = 0; identifierid < num_rands; identifierid++) + if (id == II_rand[identifierid]) + break; + + if (identifierid >= num_rands) + return; + + const FunctionProtoType *FTP + = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); + if (!FTP) + return; + + if (FTP->getNumArgs() == 1) { + // Is the argument an 'unsigned short *'? + // (Actually any integer type is allowed.) + const PointerType *PT = dyn_cast<PointerType>(FTP->getArgType(0)); + if (!PT) + return; + + if (! PT->getPointeeType()->isIntegerType()) + return; + } + else if (FTP->getNumArgs() != 0) + return; + + // Issue a warning. + llvm::SmallString<256> buf1; + llvm::raw_svector_ostream os1(buf1); + os1 << '\'' << FD << "' is a poor random number generator"; + + llvm::SmallString<256> buf2; + llvm::raw_svector_ostream os2(buf2); + os2 << "Function '" << FD + << "' is obsolete because it implements a poor random number generator." + << " Use 'arc4random' instead"; + + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: 'random' should not be used +// Originally: <rdar://problem/63371000> +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_random(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_random, "random")) + return; + + const FunctionProtoType *FTP + = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); + if (!FTP) + return; + + // Verify that the function takes no argument. + if (FTP->getNumArgs() != 0) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("'random' is not a secure random number generator", + "Security", + "The 'random' function produces a sequence of values that " + "an adversary may be able to predict. Use 'arc4random' " + "instead", CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Should check whether privileges are dropped successfully. +// Originally: <rdar://problem/6337132> +//===----------------------------------------------------------------------===// + +void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + if (II_setid[0] == NULL) { + static const char * const identifiers[num_setids] = { + "setuid", "setgid", "seteuid", "setegid", + "setreuid", "setregid" + }; + + for (size_t i = 0; i < num_setids; i++) + II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); + } + + const IdentifierInfo *id = FD->getIdentifier(); + size_t identifierid; + + for (identifierid = 0; identifierid < num_setids; identifierid++) + if (id == II_setid[identifierid]) + break; + + if (identifierid >= num_setids) + return; + + const FunctionProtoType *FTP + = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); + if (!FTP) + return; + + // Verify that the function takes one or two arguments (depending on + // the function). + if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) + return; + + // The arguments must be integers. + for (unsigned i = 0; i < FTP->getNumArgs(); i++) + if (! FTP->getArgType(i)->isIntegerType()) + return; + + // Issue a warning. + llvm::SmallString<256> buf1; + llvm::raw_svector_ostream os1(buf1); + os1 << "Return value is not checked in call to '" << FD << '\''; + + llvm::SmallString<256> buf2; + llvm::raw_svector_ostream os2(buf2); + os2 << "The return value from the call to '" << FD + << "' is not checked. If an error occurs in '" << FD + << "', the following code may execute with unexpected privileges"; + + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// SecuritySyntaxChecker +//===----------------------------------------------------------------------===// + +namespace { +class SecuritySyntaxChecker : public CheckerV2<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + WalkAST walker(BR); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { + mgr.registerChecker<SecuritySyntaxChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp new file mode 100644 index 0000000..d46ac81 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -0,0 +1,87 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a check for unintended use of sizeof() on pointer +// expressions. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/StmtVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + +public: + WalkAST(BugReporter &br) : BR(br) {} + void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitChildren(Stmt *S); +}; +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +// CWE-467: Use of sizeof() on a Pointer Type +void WalkAST::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { + if (!E->isSizeOf()) + return; + + // If an explicit type is used in the code, usually the coder knows what he is + // doing. + if (E->isArgumentType()) + return; + + QualType T = E->getTypeOfArgument(); + if (T->isPointerType()) { + + // Many false positives have the form 'sizeof *p'. This is reasonable + // because people know what they are doing when they intentionally + // dereference the pointer. + Expr *ArgEx = E->getArgumentExpr(); + if (!isa<DeclRefExpr>(ArgEx->IgnoreParens())) + return; + + SourceRange R = ArgEx->getSourceRange(); + BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", + "Logic", + "The code calls sizeof() on a pointer type. " + "This can produce an unexpected result.", + E->getLocStart(), &R, 1); + } +} + +//===----------------------------------------------------------------------===// +// SizeofPointerChecker +//===----------------------------------------------------------------------===// + +namespace { +class SizeofPointerChecker : public CheckerV2<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + WalkAST walker(BR); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerSizeofPointerChecker(CheckerManager &mgr) { + mgr.registerChecker<SizeofPointerChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td new file mode 100644 index 0000000..1dc7486 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -0,0 +1,197 @@ +//===--- Checkers.td - Static Analyzer Checkers -===-----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +include "clang/StaticAnalyzer/Checkers/CheckerBase.td" + +//===----------------------------------------------------------------------===// +// Packages. +//===----------------------------------------------------------------------===// + +def Core : Package<"core">; +def Cocoa : Package<"cocoa">; +def Unix : Package<"unix">; +def MacOSX : Package<"macosx">; + +def CoreExperimental : Package<"experimental">, + InPackage<Core>, Hidden; + +def CocoaExperimental : Package<"experimental">, + InPackage<Cocoa>, Hidden; + +def UnixExperimental : Package<"experimental">, + InPackage<Unix>, Hidden; + +def LLVM : Package<"llvm">; +def Debug : Package<"debug">; + +//===----------------------------------------------------------------------===// +// Groups. +//===----------------------------------------------------------------------===// + +def AllExperimental : CheckerGroup<"all-experimental">, + Hidden; + +//===----------------------------------------------------------------------===// +// Checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = Cocoa in { + +def ObjCSelfInitChecker : Checker<"SelfInit">, + HelpText<"Check that 'self' is propely initialized inside an initializer method">, + DescFile<"ObjCSelfInitChecker.cpp">; + +def ObjCAtSyncChecker : Checker<"AtSync">, + HelpText<"Check for null pointers used as mutexes for @synchronized">, + DescFile<"ObjCAtSyncChecker.cpp">; + +def NilArgChecker : Checker<"NilArg">, + HelpText<"Check for prohibited nil arguments to ObjC method calls">, + DescFile<"BasicObjCFoundationChecks.cpp">; + +def ClassReleaseChecker : Checker<"ClassRelease">, + HelpText<"Check for sending 'retain', 'release', or 'autorelease' directly to a Class">, + DescFile<"BasicObjCFoundationChecks.cpp">; + +def NSAutoreleasePoolChecker : Checker<"NSAutoreleasePool">, + HelpText<"Warn for subpar uses of NSAutoreleasePool">, + DescFile<"NSAutoreleasePoolChecker.cpp">; + +def ObjCMethSigsChecker : Checker<"MethodSigs">, + HelpText<"Warn about Objective-C method signatures with type incompatibilities">, + DescFile<"CheckObjCInstMethSignature.cpp">; + +def ObjCUnusedIvarsChecker : Checker<"UnusedIvars">, + HelpText<"Warn about private ivars that are never used">, + DescFile<"ObjCUnusedIVarsChecker.cpp">; + +} + +def StackAddrLeakChecker : Checker<"StackAddrLeak">, + InPackage<Core>, + HelpText<"Check that addresses to stack memory are not leaked outside the function">, + DescFile<"StackAddrLeakChecker.cpp">; + +def DeadStoresChecker : Checker<"DeadStores">, + InPackage<Core>, + HelpText<"Check for stores to dead variables">, + DescFile<"DeadStoresChecker.cpp">; + +def UnixAPIChecker : Checker<"API">, + InPackage<Unix>, + HelpText<"Check calls to various UNIX/Posix functions">, + DescFile<"UnixAPIChecker.cpp">; + +def MacOSXAPIChecker : Checker<"API">, + InPackage<MacOSX>, + HelpText<"Check calls to various MacOSXAPIChecker">, + DescFile<"MacOSXAPIChecker.cpp">; + +def CFNumberCreateChecker : Checker<"CFNumber">, + InPackage<MacOSX>, + HelpText<"Check for CFNumberCreate">, + DescFile<"BasicObjCFoundationChecks.cpp">; + +def CFRetainReleaseChecker : Checker<"CFRetainRelease">, + InPackage<MacOSX>, + HelpText<"Check for null arguments to CFRetain/CFRelease">, + DescFile<"BasicObjCFoundationChecks.cpp">; + +def LLVMConventionsChecker : Checker<"Conventions">, + InPackage<LLVM>, + HelpText<"Check code for LLVM codebase conventions">, + DescFile<"LLVMConventionsChecker.cpp">; + +def LiveVariablesDumper : Checker<"DumpLiveVars">, + InPackage<Debug>, + HelpText<"Print results of live variable analysis">, + DescFile<"DebugCheckers.cpp">; + +def CFGViewer : Checker<"ViewCFG">, + InPackage<Debug>, + HelpText<"View Control-Flow Graphs using GraphViz">, + DescFile<"DebugCheckers.cpp">; + +def CFGDumper : Checker<"DumpCFG">, + InPackage<Debug>, + HelpText<"Display Control-Flow Graphs">, + DescFile<"DebugCheckers.cpp">; + +//===----------------------------------------------------------------------===// +// Hidden experimental checkers. +//===----------------------------------------------------------------------===// + +let Group = AllExperimental in { + +def CStringChecker : Checker<"CString">, + InPackage<CoreExperimental>, + HelpText<"Check calls to functions in <string.h>">, + DescFile<"CStringChecker.cpp">; + +def UnreachableCodeChecker : Checker<"UnreachableCode">, + InPackage<CoreExperimental>, + HelpText<"Check unreachable code">, + DescFile<"UnreachableCodeChecker.cpp">; + +def IdempotentOperationChecker : Checker<"IdempotentOps">, + InPackage<CoreExperimental>, + HelpText<"Warn about idempotent operations">, + DescFile<"IdempotentOperationChecker.cpp">; + +def CastToStructChecker : Checker<"CastToStruct">, + InPackage<CoreExperimental>, + HelpText<"Check for cast from non-struct pointer to struct pointer">, + DescFile<"CastToStructChecker.cpp">; + +def FixedAddressChecker : Checker<"FixedAddr">, + InPackage<CoreExperimental>, + HelpText<"Check for assignment of a fixed address to a pointer">, + DescFile<"FixedAddressChecker.cpp">; + +def PointerArithChecker : Checker<"PointerArithm">, + InPackage<CoreExperimental>, + HelpText<"Check for pointer arithmetic on locations other than array elements">, + DescFile<"PointerArithChecker">; + +def PointerSubChecker : Checker<"PointerSub">, + InPackage<CoreExperimental>, + HelpText<"Check for pointer subtractions on two pointers pointing to different memory chunks">, + DescFile<"PointerSubChecker">; + +def SizeofPointerChecker : Checker<"SizeofPtr">, + InPackage<CoreExperimental>, + HelpText<"Warn about unintended use of sizeof() on pointer expressions">, + DescFile<"CheckSizeofPointer.cpp">; + +def SecuritySyntaxChecker : Checker<"SecuritySyntactic">, + InPackage<CoreExperimental>, + HelpText<"Perform quick security checks that require no data flow">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + +def ObjCDeallocChecker : Checker<"Dealloc">, + InPackage<CocoaExperimental>, + HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, + DescFile<"CheckObjCDealloc.cpp">; + +def ChrootChecker : Checker<"Chroot">, + InPackage<UnixExperimental>, + HelpText<"Check improper use of chroot">, + DescFile<"ChrootChecker.cpp">; + +def PthreadLockChecker : Checker<"PthreadLock">, + InPackage<UnixExperimental>, + HelpText<"Simple lock -> unlock checker">, + DescFile<"PthreadLockChecker.cpp">; + +def StreamChecker : Checker<"Stream">, + InPackage<UnixExperimental>, + HelpText<"Check stream handling functions">, + DescFile<"StreamChecker.cpp">; + +} diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp new file mode 100644 index 0000000..36e76d0 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -0,0 +1,167 @@ +//===- Chrootchecker.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. +// +//===----------------------------------------------------------------------===// +// +// This file defines chroot checker, which checks improper use of chroot. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +using namespace clang; +using namespace ento; + +namespace { + +// enum value that represent the jail state +enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED }; + +bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; } +//bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; } + +// This checker checks improper use of chroot. +// The state transition: +// NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED +// | | +// ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)-- +// | | +// bug<--foo()-- JAIL_ENTERED<--foo()-- +class ChrootChecker : public CheckerVisitor<ChrootChecker> { + IdentifierInfo *II_chroot, *II_chdir; + // This bug refers to possibly break out of a chroot() jail. + BuiltinBug *BT_BreakJail; + +public: + ChrootChecker() : II_chroot(0), II_chdir(0), BT_BreakJail(0) {} + + static void *getTag() { + static int x; + return &x; + } + + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + virtual void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + +private: + void Chroot(CheckerContext &C, const CallExpr *CE); + void Chdir(CheckerContext &C, const CallExpr *CE); +}; + +} // end anonymous namespace + +static void RegisterChrootChecker(ExprEngine &Eng) { + Eng.registerCheck(new ChrootChecker()); +} + +void ento::registerChrootChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterChrootChecker); +} + +bool ChrootChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + 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); + return true; + } + if (FD->getIdentifier() == II_chdir) { + Chdir(C, CE); + return true; + } + + return false; +} + +void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + GRStateManager &Mgr = state->getStateManager(); + + // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in + // the GDM. + state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED); + C.addTransition(state); +} + +void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + GRStateManager &Mgr = state->getStateManager(); + + // If there are no jail state in the GDM, just return. + const void* k = state->FindGDM(ChrootChecker::getTag()); + if (!k) + return; + + // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. + const Expr *ArgExpr = CE->getArg(0); + SVal ArgVal = state->getSVal(ArgExpr); + + if (const MemRegion *R = ArgVal.getAsRegion()) { + R = R->StripCasts(); + if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) { + const StringLiteral* Str = StrRegion->getStringLiteral(); + if (Str->getString() == "/") + state = Mgr.addGDM(state, ChrootChecker::getTag(), + (void*) JAIL_ENTERED); + } + } + + C.addTransition(state); +} + +// Check the jail state before any function call except chroot and chdir(). +void ChrootChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + 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"); + + // Ingnore chroot and chdir. + if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir) + return; + + // If jail state is ROOT_CHANGED, generate BugReport. + void* const* k = state->FindGDM(ChrootChecker::getTag()); + if (k) + if (isRootChanged((intptr_t) *k)) + if (ExplodedNode *N = C.generateNode()) { + if (!BT_BreakJail) + BT_BreakJail = new BuiltinBug("Break out of jail", + "No call of chdir(\"/\") immediately " + "after chroot"); + BugReport *R = new BugReport(*BT_BreakJail, + BT_BreakJail->getDescription(), N); + C.EmitReport(R); + } + + return; +} diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp new file mode 100644 index 0000000..94f200f --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp @@ -0,0 +1,137 @@ +//===--- ClangSACheckerProvider.cpp - Clang SA Checkers Provider ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the CheckerProvider for the checkers defined in +// libclangStaticAnalyzerCheckers. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckerProvider.h" +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/CheckerProvider.h" +#include "llvm/ADT/DenseSet.h" + +using namespace clang; +using namespace ento; + +namespace { + +/// \brief Provider for all the checkers in libclangStaticAnalyzerCheckers. +class ClangSACheckerProvider : public CheckerProvider { +public: + virtual void registerCheckers(CheckerManager &checkerMgr, + CheckerOptInfo *checkOpts, unsigned numCheckOpts); +}; + +} + +CheckerProvider *ento::createClangSACheckerProvider() { + return new ClangSACheckerProvider(); +} + +namespace { + +struct StaticCheckerInfoRec { + const char *FullName; + void (*RegFunc)(CheckerManager &mgr); + bool Hidden; +}; + +} // end anonymous namespace. + +static const StaticCheckerInfoRec StaticCheckerInfo[] = { +#define GET_CHECKERS +#define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,HIDDEN) \ + { FULLNAME, register##CLASS, HIDDEN }, +#include "Checkers.inc" + { 0, 0, 0} +#undef CHECKER +#undef GET_CHECKERS +}; + +namespace { + +struct CheckNameOption { + const char *Name; + const short *Members; + const short *SubGroups; + bool Hidden; +}; + +} // end anonymous namespace. + +#define GET_MEMBER_ARRAYS +#include "Checkers.inc" +#undef GET_MEMBER_ARRAYS + +// The table of check name options, sorted by name for fast binary lookup. +static const CheckNameOption CheckNameTable[] = { +#define GET_CHECKNAME_TABLE +#include "Checkers.inc" +#undef GET_CHECKNAME_TABLE +}; +static const size_t + CheckNameTableSize = sizeof(CheckNameTable) / sizeof(CheckNameTable[0]); + +static bool CheckNameOptionCompare(const CheckNameOption &LHS, + const CheckNameOption &RHS) { + return strcmp(LHS.Name, RHS.Name) < 0; +} + +static void collectCheckers(const CheckNameOption *checkName, + bool enable, + llvm::DenseSet<const StaticCheckerInfoRec *> &checkers, + bool collectHidden) { + if (checkName->Hidden && !collectHidden) + return; + + if (const short *member = checkName->Members) { + if (enable) { + if (collectHidden || !StaticCheckerInfo[*member].Hidden) + checkers.insert(&StaticCheckerInfo[*member]); + } else { + for (; *member != -1; ++member) + checkers.erase(&StaticCheckerInfo[*member]); + } + } + + // Enable/disable all subgroups along with this one. + if (const short *subGroups = checkName->SubGroups) { + for (; *subGroups != -1; ++subGroups) + collectCheckers(&CheckNameTable[*subGroups], enable, checkers, + collectHidden && checkName->Hidden); + } +} + +static void collectCheckers(CheckerOptInfo &opt, + llvm::DenseSet<const StaticCheckerInfoRec *> &checkers) { + const char *optName = opt.getName(); + CheckNameOption key = { optName, 0, 0, false }; + const CheckNameOption *found = + std::lower_bound(CheckNameTable, CheckNameTable + CheckNameTableSize, key, + CheckNameOptionCompare); + if (found == CheckNameTable + CheckNameTableSize || + strcmp(found->Name, optName) != 0) + return; // Check name not found. + + opt.claim(); + collectCheckers(found, opt.isEnabled(), checkers, /*collectHidden=*/true); +} + +void ClangSACheckerProvider::registerCheckers(CheckerManager &checkerMgr, + CheckerOptInfo *checkOpts, unsigned numCheckOpts) { + llvm::DenseSet<const StaticCheckerInfoRec *> enabledCheckers; + for (unsigned i = 0; i != numCheckOpts; ++i) + collectCheckers(checkOpts[i], enabledCheckers); + for (llvm::DenseSet<const StaticCheckerInfoRec *>::iterator + I = enabledCheckers.begin(), E = enabledCheckers.end(); I != E; ++I) { + (*I)->RegFunc(checkerMgr); + } +} diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h new file mode 100644 index 0000000..f6c8011 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h @@ -0,0 +1,29 @@ +//===--- ClangSACheckerProvider.h - Clang SA Checkers Provider --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the entry point for creating the provider for the checkers defined +// in libclangStaticAnalyzerCheckers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_CHECKERS_CLANGSACHECKERPROVIDER_H +#define LLVM_CLANG_SA_CHECKERS_CLANGSACHECKERPROVIDER_H + +namespace clang { + +namespace ento { + class CheckerProvider; + +CheckerProvider *createClangSACheckerProvider(); + +} // end ento namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h new file mode 100644 index 0000000..73239f5 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h @@ -0,0 +1,34 @@ +//===--- ClangSACheckers.h - Registration 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. +// +//===----------------------------------------------------------------------===// +// +// Declares the registation functions for the checkers defined in +// libclangStaticAnalyzerCheckers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H +#define LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H + +namespace clang { + +namespace ento { +class CheckerManager; + +#define GET_CHECKERS +#define CHECKER(FULLNAME,CLASS,CXXFILE,HELPTEXT,HIDDEN) \ + void register##CLASS(CheckerManager &mgr); +#include "Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + +} // end ento namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp new file mode 100644 index 0000000..3b39372 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -0,0 +1,363 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a DeadStores, a flow-sensitive checker that looks for +// stores to variables that are no longer live. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ParentMap.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; +using namespace ento; + +namespace { + +// FIXME: Eventually migrate into its own file, and have it managed by +// AnalysisManager. +class ReachableCode { + const CFG &cfg; + llvm::BitVector reachable; +public: + ReachableCode(const CFG &cfg) + : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {} + + void computeReachableBlocks(); + + bool isReachable(const CFGBlock *block) const { + return reachable[block->getBlockID()]; + } +}; +} + +void ReachableCode::computeReachableBlocks() { + if (!cfg.getNumBlockIDs()) + return; + + llvm::SmallVector<const CFGBlock*, 10> worklist; + worklist.push_back(&cfg.getEntry()); + + while (!worklist.empty()) { + const CFGBlock *block = worklist.back(); + worklist.pop_back(); + llvm::BitVector::reference isReachable = reachable[block->getBlockID()]; + if (isReachable) + continue; + isReachable = true; + for (CFGBlock::const_succ_iterator i = block->succ_begin(), + e = block->succ_end(); i != e; ++i) + if (const CFGBlock *succ = *i) + worklist.push_back(succ); + } +} + +namespace { +class DeadStoreObs : public LiveVariables::ObserverTy { + const CFG &cfg; + ASTContext &Ctx; + BugReporter& BR; + ParentMap& Parents; + llvm::SmallPtrSet<VarDecl*, 20> Escaped; + llvm::OwningPtr<ReachableCode> reachableCode; + const CFGBlock *currentBlock; + + enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; + +public: + DeadStoreObs(const CFG &cfg, ASTContext &ctx, + BugReporter& br, ParentMap& parents, + llvm::SmallPtrSet<VarDecl*, 20> &escaped) + : cfg(cfg), Ctx(ctx), BR(br), Parents(parents), + Escaped(escaped), currentBlock(0) {} + + virtual ~DeadStoreObs() {} + + void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) { + if (Escaped.count(V)) + return; + + // Compute reachable blocks within the CFG for trivial cases + // where a bogus dead store can be reported because itself is unreachable. + if (!reachableCode.get()) { + reachableCode.reset(new ReachableCode(cfg)); + reachableCode->computeReachableBlocks(); + } + + if (!reachableCode->isReachable(currentBlock)) + return; + + const std::string &name = V->getNameAsString(); + + const char* BugType = 0; + std::string msg; + + switch (dsk) { + default: + assert(false && "Impossible dead store type."); + + case DeadInit: + BugType = "Dead initialization"; + msg = "Value stored to '" + name + + "' during its initialization is never read"; + break; + + case DeadIncrement: + BugType = "Dead increment"; + case Standard: + if (!BugType) BugType = "Dead assignment"; + msg = "Value stored to '" + name + "' is never read"; + break; + + case Enclosing: + // Don't report issues in this case, e.g.: "if (x = foo())", + // where 'x' is unused later. We have yet to see a case where + // this is a real bug. + return; + } + + BR.EmitBasicReport(BugType, "Dead store", msg, L, R); + } + + void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, + DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + if (!VD->hasLocalStorage()) + return; + // Reference types confuse the dead stores checker. Skip them + // for now. + if (VD->getType()->getAs<ReferenceType>()) + return; + + if (!Live(VD, AD) && + !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) + Report(VD, dsk, Ex->getSourceRange().getBegin(), + Val->getSourceRange()); + } + + void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) + CheckVarDecl(VD, DR, Val, dsk, AD, Live); + } + + bool isIncrement(VarDecl* VD, BinaryOperator* B) { + if (B->isCompoundAssignmentOp()) + return true; + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS); + + if (!BRHS) + return false; + + DeclRefExpr *DR; + + if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + return false; + } + + virtual void ObserveStmt(Stmt* S, const CFGBlock *block, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + currentBlock = block; + + // Skip statements in macros. + if (S->getLocStart().isMacroID()) + return; + + // Only cover dead stores from regular assignments. ++/-- dead stores + // have never flagged a real bug. + if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { + if (!B->isAssignmentOp()) return; // Skip non-assignments. + + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS())) + if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + // Special case: check for assigning null to a pointer. + // This is a common form of defensive programming. + QualType T = VD->getType(); + if (T->isPointerType() || T->isObjCObjectPointerType()) { + if (B->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return; + } + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + // Special case: self-assignments. These are often used to shut up + // "unused variable" compiler warnings. + if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS)) + if (VD == dyn_cast<VarDecl>(RhsDR->getDecl())) + return; + + // Otherwise, issue a warning. + DeadStoreKind dsk = Parents.isConsumedExpr(B) + ? Enclosing + : (isIncrement(VD,B) ? DeadIncrement : Standard); + + CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live); + } + } + else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) { + if (!U->isIncrementOp() || U->isPrefix()) + return; + + Stmt *parent = Parents.getParentIgnoreParenCasts(U); + if (!parent || !isa<ReturnStmt>(parent)) + return; + + Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); + + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex)) + CheckDeclRef(DR, U, DeadIncrement, AD, Live); + } + else if (DeclStmt* DS = dyn_cast<DeclStmt>(S)) + // Iterate through the decls. Warn if any initializers are complex + // expressions that are not live (never used). + for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); + DI != DE; ++DI) { + + VarDecl* V = dyn_cast<VarDecl>(*DI); + + if (!V) + continue; + + if (V->hasLocalStorage()) { + // Reference types confuse the dead stores checker. Skip them + // for now. + if (V->getType()->getAs<ReferenceType>()) + return; + + if (Expr* E = V->getInit()) { + // Don't warn on C++ objects (yet) until we can show that their + // constructors/destructors don't have side effects. + if (isa<CXXConstructExpr>(E)) + return; + + if (isa<ExprWithCleanups>(E)) + return; + + // A dead initialization is a variable that is dead after it + // is initialized. We don't flag warnings for those variables + // marked 'unused'. + if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) { + // Special case: check for initializations with constants. + // + // e.g. : int x = 0; + // + // If x is EVER assigned a new value later, don't issue + // a warning. This is because such initialization can be + // due to defensive programming. + if (E->isConstantInitializer(Ctx, false)) + return; + + if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + // Special case: check for initialization from constant + // variables. + // + // e.g. extern const int MyConstant; + // int x = MyConstant; + // + if (VD->hasGlobalStorage() && + VD->getType().isConstQualified()) + return; + // Special case: check for initialization from scalar + // parameters. This is often a form of defensive + // programming. Non-scalars are still an error since + // because it more likely represents an actual algorithmic + // bug. + if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType()) + return; + } + + Report(V, DeadInit, V->getLocation(), E->getSourceRange()); + } + } + } + } + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Driver function to invoke the Dead-Stores checker on a CFG. +//===----------------------------------------------------------------------===// + +namespace { +class FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{ + CFG *cfg; +public: + FindEscaped(CFG *c) : cfg(c) {} + + CFG& getCFG() { return *cfg; } + + llvm::SmallPtrSet<VarDecl*, 20> Escaped; + + void VisitUnaryOperator(UnaryOperator* U) { + // Check for '&'. Any VarDecl whose value has its address-taken we + // treat as escaped. + Expr* E = U->getSubExpr()->IgnoreParenCasts(); + if (U->getOpcode() == UO_AddrOf) + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E)) + if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) { + Escaped.insert(VD); + return; + } + Visit(E); + } +}; +} // end anonymous namespace + + +//===----------------------------------------------------------------------===// +// DeadStoresChecker +//===----------------------------------------------------------------------===// + +namespace { +class DeadStoresChecker : public CheckerV2<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (LiveVariables *L = mgr.getLiveVariables(D)) { + CFG &cfg = *mgr.getCFG(D); + ParentMap &pmap = mgr.getParentMap(D); + FindEscaped FS(&cfg); + FS.getCFG().VisitBlockStmts(FS); + DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped); + L->runOnAllBlocks(cfg, &A); + } + } +}; +} + +void ento::registerDeadStoresChecker(CheckerManager &mgr) { + mgr.registerChecker<DeadStoresChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp new file mode 100644 index 0000000..091d99b --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -0,0 +1,80 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a checkers that display debugging information. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/Analysis/Analyses/LiveVariables.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// LiveVariablesDumper +//===----------------------------------------------------------------------===// + +namespace { +class LiveVariablesDumper : public CheckerV2<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (LiveVariables* L = mgr.getLiveVariables(D)) { + L->dumpBlockLiveness(mgr.getSourceManager()); + } + } +}; +} + +void ento::registerLiveVariablesDumper(CheckerManager &mgr) { + mgr.registerChecker<LiveVariablesDumper>(); +} + +//===----------------------------------------------------------------------===// +// CFGViewer +//===----------------------------------------------------------------------===// + +namespace { +class CFGViewer : public CheckerV2<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->viewCFG(mgr.getLangOptions()); + } + } +}; +} + +void ento::registerCFGViewer(CheckerManager &mgr) { + mgr.registerChecker<CFGViewer>(); +} + +//===----------------------------------------------------------------------===// +// CFGDumper +//===----------------------------------------------------------------------===// + +namespace { +class CFGDumper : public CheckerV2<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->dump(mgr.getLangOptions()); + } + } +}; +} + +void ento::registerCFGDumper(CheckerManager &mgr) { + mgr.registerChecker<CFGDumper>(); +} diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp new file mode 100644 index 0000000..606ac4a --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -0,0 +1,205 @@ +//== NullDerefChecker.cpp - Null dereference 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 NullDerefChecker, a builtin check in ExprEngine that performs +// checks for null pointers at loads and stores. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class DereferenceChecker : public Checker { + BuiltinBug *BT_null; + BuiltinBug *BT_undef; + llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes; +public: + DereferenceChecker() : BT_null(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void visitLocation(CheckerContext &C, const Stmt *S, SVal location, + bool isLoad); + + std::pair<ExplodedNode * const*, ExplodedNode * const*> + getImplicitNodes() const { + return std::make_pair(ImplicitNullDerefNodes.data(), + ImplicitNullDerefNodes.data() + + ImplicitNullDerefNodes.size()); + } + void AddDerefSource(llvm::raw_ostream &os, + llvm::SmallVectorImpl<SourceRange> &Ranges, + const Expr *Ex, bool loadedFrom = false); +}; +} // end anonymous namespace + +void ento::RegisterDereferenceChecker(ExprEngine &Eng) { + Eng.registerCheck(new DereferenceChecker()); +} + +std::pair<ExplodedNode * const *, ExplodedNode * const *> +ento::GetImplicitNullDereferences(ExprEngine &Eng) { + DereferenceChecker *checker = Eng.getChecker<DereferenceChecker>(); + if (!checker) + return std::make_pair((ExplodedNode * const *) 0, + (ExplodedNode * const *) 0); + return checker->getImplicitNodes(); +} + +void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, + llvm::SmallVectorImpl<SourceRange> &Ranges, + const Expr *Ex, + bool loadedFrom) { + Ex = Ex->IgnoreParenLValueCasts(); + switch (Ex->getStmtClass()) { + default: + return; + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + os << " (" << (loadedFrom ? "loaded from" : "from") + << " variable '" << VD->getName() << "')"; + Ranges.push_back(DR->getSourceRange()); + } + return; + } + case Stmt::MemberExprClass: { + const MemberExpr *ME = cast<MemberExpr>(Ex); + os << " (" << (loadedFrom ? "loaded from" : "via") + << " field '" << ME->getMemberNameInfo() << "')"; + SourceLocation L = ME->getMemberLoc(); + Ranges.push_back(SourceRange(L, L)); + break; + } + } +} + +void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S, + SVal l, bool isLoad) { + // Check for dereference of an undefined value. + if (l.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_undef) + BT_undef = new BuiltinBug("Dereference of undefined pointer value"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + C.EmitReport(report); + } + return; + } + + DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); + + // Check for null dereferences. + if (!isa<Loc>(location)) + return; + + const GRState *state = C.getState(); + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(location); + + // The explicit NULL case. + if (nullState) { + if (!notNullState) { + // Generate an error node. + ExplodedNode *N = C.generateSink(nullState); + if (!N) + return; + + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null = new BuiltinBug("Dereference of null pointer"); + + llvm::SmallString<100> buf; + llvm::SmallVector<SourceRange, 2> Ranges; + + // Walk through lvalue casts to get the original expression + // that syntactically caused the load. + if (const Expr *expr = dyn_cast<Expr>(S)) + S = expr->IgnoreParenLValueCasts(); + + switch (S->getStmtClass()) { + case Stmt::ArraySubscriptExprClass: { + llvm::raw_svector_ostream os(buf); + os << "Array access"; + const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); + AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts()); + os << " results in a null pointer dereference"; + break; + } + case Stmt::UnaryOperatorClass: { + llvm::raw_svector_ostream os(buf); + os << "Dereference of null pointer"; + const UnaryOperator *U = cast<UnaryOperator>(S); + AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), true); + break; + } + case Stmt::MemberExprClass: { + const MemberExpr *M = cast<MemberExpr>(S); + if (M->isArrow()) { + llvm::raw_svector_ostream os(buf); + os << "Access to field '" << M->getMemberNameInfo() + << "' results in a dereference of a null pointer"; + AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), true); + } + break; + } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); + if (const DeclRefExpr *DR = + dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + llvm::raw_svector_ostream os(buf); + os << "Instance variable access (via '" << VD->getName() + << "') results in a null pointer dereference"; + } + } + Ranges.push_back(IV->getSourceRange()); + break; + } + default: + break; + } + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, + buf.empty() ? BT_null->getDescription():buf.str(), + N); + + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + + for (llvm::SmallVectorImpl<SourceRange>::iterator + I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) + report->addRange(*I); + + C.EmitReport(report); + return; + } + else { + // Otherwise, we have the case where the location could either be + // null or not-null. Record the error node as an "implicit" null + // dereference. + if (ExplodedNode *N = C.generateSink(nullState)) + ImplicitNullDerefNodes.push_back(N); + } + } + + // From this point forward, we know that the location is not null. + C.addTransition(notNullState); +} diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp new file mode 100644 index 0000000..20cc904 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -0,0 +1,86 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This defines DivZeroChecker, a builtin check in ExprEngine that performs +// checks for division by zeros. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class DivZeroChecker : public CheckerVisitor<DivZeroChecker> { + BuiltinBug *BT; +public: + DivZeroChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void ento::RegisterDivZeroChecker(ExprEngine &Eng) { + Eng.registerCheck(new DivZeroChecker()); +} + +void *DivZeroChecker::getTag() { + static int x; + return &x; +} + +void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + if (Op != BO_Div && + Op != BO_Rem && + Op != BO_DivAssign && + Op != BO_RemAssign) + return; + + if (!B->getRHS()->getType()->isIntegerType() || + !B->getRHS()->getType()->isScalarType()) + return; + + SVal Denom = C.getState()->getSVal(B->getRHS()); + const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); + + // Divide-by-undefined handled in the generic checking for uses of + // undefined values. + if (!DV) + return; + + // Check for divide by zero. + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); + + if (stateZero && !stateNotZero) { + if (ExplodedNode *N = C.generateSink(stateZero)) { + if (!BT) + BT = new BuiltinBug("Division by zero"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDenomExpr(N)); + + C.EmitReport(R); + } + return; + } + + // If we get here, then the denom should not be zero. We abandon the implicit + // zero denom case for now. + C.addTransition(stateNotZero); +} diff --git a/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp b/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp new file mode 100644 index 0000000..d9bb480 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp @@ -0,0 +1,37 @@ +//=-- ExperimentalChecks.h ----------------------------------------*- 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 functions to instantiate and register experimental +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "ExperimentalChecks.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" + +using namespace clang; +using namespace ento; + +void ento::RegisterExperimentalChecks(ExprEngine &Eng) { + // These are checks that never belong as internal checks + // within ExprEngine. + RegisterMallocChecker(Eng); // ArrayBoundChecker depends on this. +} + +void ento::RegisterExperimentalInternalChecks(ExprEngine &Eng) { + // These are internal checks that should eventually migrate to + // RegisterInternalChecks() once they have been further tested. + + // Note that this must be registered after ReturnStackAddresEngsChecker. + RegisterReturnPointerRangeChecker(Eng); + + RegisterArrayBoundChecker(Eng); + RegisterCastSizeChecker(Eng); +} diff --git a/lib/StaticAnalyzer/Checkers/ExperimentalChecks.h b/lib/StaticAnalyzer/Checkers/ExperimentalChecks.h new file mode 100644 index 0000000..1f38ad7 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ExperimentalChecks.h @@ -0,0 +1,31 @@ +//=-- ExperimentalChecks.h ----------------------------------------*- 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 functions to instantiate and register experimental +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ExprEngine_EXPERIMENTAL_CHECKS +#define LLVM_CLANG_GR_ExprEngine_EXPERIMENTAL_CHECKS + +namespace clang { + +namespace ento { + +class ExprEngine; + +void RegisterAnalyzerStatsChecker(ExprEngine &Eng); +void RegisterMallocChecker(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp new file mode 100644 index 0000000..ab8d564 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -0,0 +1,3655 @@ +//=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- 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 meta-engine for path-sensitive dataflow analysis that +// is built on GREngine, but provides the boilerplate to execute transfer +// functions and build the ExplodedGraph at the expression level. +// +//===----------------------------------------------------------------------===// + +// FIXME: Restructure checker registration. +#include "InternalChecks.h" + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/PrettyStackTrace.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/ImmutableList.h" + +#ifndef NDEBUG +#include "llvm/Support/GraphWriter.h" +#endif + +using namespace clang; +using namespace ento; +using llvm::dyn_cast; +using llvm::dyn_cast_or_null; +using llvm::cast; +using llvm::APSInt; + +namespace { + // Trait class for recording returned expression in the state. + struct ReturnExpr { + static int TagInt; + typedef const Stmt *data_type; + }; + int ReturnExpr::TagInt; +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(0, &II); +} + +//===----------------------------------------------------------------------===// +// Checker worklist routines. +//===----------------------------------------------------------------------===// + +void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, CallbackKind Kind) { + + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for the provided <CallbackKind, Stmt kind>. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr<CheckersOrdered> NewCO; + + // The cache key is made up of the and the callback kind (pre- or post-visit) + // and the statement kind. + CallbackTag K = GetCallbackTag(Kind, S->getStmtClass()); + + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // statement kind, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + if (CO->empty()) { + // If there are no checkers, return early without doing any + // more work. + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + unsigned checkersEvaluated = 0; + + for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I) { + // If all nodes are sunk, bail out early. + if (PrevSet->empty()) + break; + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + void *tag = I->first; + Checker *checker = I->second; + bool respondsToCallback = true; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) { + + checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, + Kind == PreVisitStmtCallback, respondsToCallback); + + } + + PrevSet = CurrSet; + + if (NewCO.get()) { + ++checkersEvaluated; + if (respondsToCallback) + NewCO->push_back(*I); + } + } + + // If we built NewCO, check if we called all the checkers. This is important + // so that we know that we accurately determined the entire set of checkers + // that responds to this callback. Note that 'checkersEvaluated' might + // not be the same as Checkers.size() if one of the Checkers generates + // a sink node. + if (NewCO.get() && checkersEvaluated == Checkers.size()) + CO_Ref = NewCO.take(); + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} + +void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, + ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + bool isPrevisit) { + + if (Checkers.empty()) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_visitObjCMessage(*CurrSet, *Builder, *this, msg, + *NI, tag, isPrevisit); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} + +void ExprEngine::CheckerEvalNilReceiver(const ObjCMessage &msg, + ExplodedNodeSet &Dst, + const GRState *state, + ExplodedNode *Pred) { + bool evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_evalNilReceiver(DstTmp, *Builder, *this, msg, Pred, state, + tag)) { + evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the Dst. + DstTmp.clear(); + } + + if (evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); +} + +// CheckerEvalCall returns true if one of the checkers processed the node. +// This may return void when all call evaluation logic goes to some checker +// in the future. +bool ExprEngine::CheckerEvalCall(const CallExpr *CE, + ExplodedNodeSet &Dst, + ExplodedNode *Pred) { + bool evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_evalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) { + evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the DstTmp set. + DstTmp.clear(); + } + + if (evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); + + return evaluated; +} + +// FIXME: This is largely copy-paste from CheckerVisit(). Need to +// unify. +void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, SVal location, + SVal val, bool isPrevisit) { + + if (Checkers.empty()) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_VisitBind(*CurrSet, *Builder, *this, StoreE, + *NI, tag, location, val, isPrevisit); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} +//===----------------------------------------------------------------------===// +// Engine construction and deletion. +//===----------------------------------------------------------------------===// + +static void RegisterInternalChecks(ExprEngine &Eng) { + // Register internal "built-in" BugTypes with the BugReporter. These BugTypes + // are different than what probably many checks will do since they don't + // create BugReports on-the-fly but instead wait until ExprEngine finishes + // analyzing a function. Generation of BugReport objects is done via a call + // to 'FlushReports' from BugReporter. + // The following checks do not need to have their associated BugTypes + // explicitly registered with the BugReporter. If they issue any BugReports, + // their associated BugType will get registered with the BugReporter + // automatically. Note that the check itself is owned by the ExprEngine + // object. + RegisterAdjustedReturnValueChecker(Eng); + // CallAndMessageChecker should be registered before AttrNonNullChecker, + // where we assume arguments are not undefined. + RegisterCallAndMessageChecker(Eng); + RegisterAttrNonNullChecker(Eng); + RegisterDereferenceChecker(Eng); + RegisterVLASizeChecker(Eng); + RegisterDivZeroChecker(Eng); + RegisterReturnUndefChecker(Eng); + RegisterUndefinedArraySubscriptChecker(Eng); + RegisterUndefinedAssignmentChecker(Eng); + RegisterUndefBranchChecker(Eng); + RegisterUndefCapturedBlockVarChecker(Eng); + RegisterUndefResultChecker(Eng); + + // This is not a checker yet. + RegisterNoReturnFunctionChecker(Eng); + RegisterBuiltinFunctionChecker(Eng); + RegisterOSAtomicChecker(Eng); +} + +ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) + : AMgr(mgr), + Engine(*this), + G(Engine.getGraph()), + Builder(NULL), + StateMgr(getContext(), mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator(), G.getAllocator(), + *this), + SymMgr(StateMgr.getSymbolManager()), + svalBuilder(StateMgr.getSValBuilder()), + EntryNode(NULL), currentStmt(NULL), + NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), + RaiseSel(GetNullarySelector("raise", getContext())), + BR(mgr, *this), TF(tf) { + // Register internal checks. + RegisterInternalChecks(*this); + + // FIXME: Eventually remove the TF object entirely. + TF->RegisterChecks(*this); + TF->RegisterPrinters(getStateManager().Printers); + + mgr.getCheckerManager()->registerCheckersToEngine(*this); + + if (mgr.shouldEagerlyTrimExplodedGraph()) { + // Enable eager node reclaimation when constructing the ExplodedGraph. + G.enableNodeReclamation(); + } +} + +ExprEngine::~ExprEngine() { + BR.FlushReports(); + delete [] NSExceptionInstanceRaiseSelectors; + + // Delete the set of checkers. + for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) + delete I->second; + + for (CheckersOrderedCache::iterator I=COCache.begin(), E=COCache.end(); + I!=E;++I) + delete I->second; +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) { + const GRState *state = StateMgr.getInitialState(InitLoc); + + // Preconditions. + + // FIXME: It would be nice if we had a more general mechanism to add + // such preconditions. Some day. + do { + const Decl *D = InitLoc->getDecl(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + // Precondition: the first argument of 'main' is an integer guaranteed + // to be > 0. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II || !(II->getName() == "main" && FD->getNumParams() > 0)) + break; + + const ParmVarDecl *PD = FD->getParamDecl(0); + QualType T = PD->getType(); + if (!T->isIntegerType()) + break; + + const MemRegion *R = state->getRegion(PD, InitLoc); + if (!R) + break; + + SVal V = state->getSVal(loc::MemRegionVal(R)); + SVal Constraint_untested = evalBinOp(state, BO_GT, V, + svalBuilder.makeZeroVal(T), + getContext().IntTy); + + DefinedOrUnknownSVal *Constraint = + dyn_cast<DefinedOrUnknownSVal>(&Constraint_untested); + + if (!Constraint) + break; + + if (const GRState *newState = state->assume(*Constraint, true)) + state = newState; + + break; + } + + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + // Precondition: 'self' is always non-null upon entry to an Objective-C + // method. + const ImplicitParamDecl *SelfD = MD->getSelfDecl(); + const MemRegion *R = state->getRegion(SelfD, InitLoc); + SVal V = state->getSVal(loc::MemRegionVal(R)); + + if (const Loc *LV = dyn_cast<Loc>(&V)) { + // Assume that the pointer value in 'self' is non-null. + state = state->assume(*LV, true); + assert(state && "'self' cannot be null"); + } + } + } while (0); + + return state; +} + +//===----------------------------------------------------------------------===// +// Top-level transfer function logic (Dispatcher). +//===----------------------------------------------------------------------===// + +/// evalAssume - Called by ConstraintManager. Used to call checker-specific +/// logic for handling assumptions on symbolic values. +const GRState *ExprEngine::processAssume(const GRState *state, SVal cond, + bool assumption) { + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for processing assumptions. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr<CheckersOrdered> NewCO; + + CallbackTag K = GetCallbackTag(processAssumeCallback); + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // statement kind, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + if (!CO->empty()) { + // Let the checkers have a crack at the assume before the transfer functions + // get their turn. + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I!=E; ++I) { + + // If any checker declares the state infeasible (or if it starts that + // way), bail out. + if (!state) + return NULL; + + Checker *C = I->second; + bool respondsToCallback = true; + + state = C->evalAssume(state, cond, assumption, &respondsToCallback); + + // Check if we're building the cache of checkers that care about + // assumptions. + if (NewCO.get() && respondsToCallback) + NewCO->push_back(*I); + } + + // If we got through all the checkers, and we built a list of those that + // care about assumptions, save it. + if (NewCO.get()) + CO_Ref = NewCO.take(); + } + + // If the state is infeasible at this point, bail out. + if (!state) + return NULL; + + return TF->evalAssume(state, cond, assumption); +} + +bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) { + CallbackTag K = GetCallbackTag(EvalRegionChangesCallback); + CheckersOrdered *CO = COCache[K]; + + if (!CO) + CO = &Checkers; + + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { + Checker *C = I->second; + if (C->wantsRegionChangeUpdate(state)) + return true; + } + + return false; +} + +const GRState * +ExprEngine::processRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) { + // FIXME: Most of this method is copy-pasted from processAssume. + + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for processing region changes. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr<CheckersOrdered> NewCO; + + CallbackTag K = GetCallbackTag(EvalRegionChangesCallback); + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // callback, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + // If there are no checkers, just return the state as is. + if (CO->empty()) + return state; + + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!state) + return NULL; + + Checker *C = I->second; + bool respondsToCallback = true; + + state = C->EvalRegionChanges(state, Begin, End, &respondsToCallback); + + // See if we're building a cache of checkers that care about region changes. + if (NewCO.get() && respondsToCallback) + NewCO->push_back(*I); + } + + // If we got through all the checkers, and we built a list of those that + // care about region changes, save it. + if (NewCO.get()) + CO_Ref = NewCO.take(); + + return state; +} + +void ExprEngine::processEndWorklist(bool hasWorkRemaining) { + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + I->second->VisitEndAnalysis(G, BR, *this); + } +} + +void ExprEngine::processCFGElement(const CFGElement E, + StmtNodeBuilder& builder) { + switch (E.getKind()) { + case CFGElement::Statement: + ProcessStmt(E.getAs<CFGStmt>(), builder); + break; + case CFGElement::Initializer: + ProcessInitializer(E.getAs<CFGInitializer>(), builder); + break; + case CFGElement::ImplicitDtor: + ProcessImplicitDtor(E.getAs<CFGImplicitDtor>(), builder); + break; + default: + // Suppress compiler warning. + llvm_unreachable("Unexpected CFGElement kind."); + } +} + +void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { + // Reclaim any unnecessary nodes in the ExplodedGraph. + G.reclaimRecentlyAllocatedNodes(); + // Recycle any unused states in the GRStateManager. + StateMgr.recycleUnusedStates(); + + currentStmt = S.getStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + currentStmt->getLocStart(), + "Error evaluating statement"); + + Builder = &builder; + EntryNode = builder.getPredecessor(); + + // Create the cleaned state. + const LocationContext *LC = EntryNode->getLocationContext(); + SymbolReaper SymReaper(LC, currentStmt, SymMgr); + + if (AMgr.shouldPurgeDead()) { + const GRState *St = EntryNode->getState(); + + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + Checker *checker = I->second; + checker->MarkLiveSymbols(St, SymReaper); + } + + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + CleanedState = StateMgr.removeDeadBindings(St, SFC, SymReaper); + } else { + CleanedState = EntryNode->getState(); + } + + // Process any special transfer function for dead symbols. + ExplodedNodeSet Tmp; + + if (!SymReaper.hasDeadSymbols()) + Tmp.Add(EntryNode); + else { + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->hasGeneratedNode); + + SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); + Builder->PurgingDeadSymbols = true; + + // FIXME: This should soon be removed. + ExplodedNodeSet Tmp2; + getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode, + CleanedState, SymReaper); + + if (Checkers.empty()) + Tmp.insert(Tmp2); + else { + ExplodedNodeSet Tmp3; + ExplodedNodeSet *SrcSet = &Tmp2; + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + ExplodedNodeSet *DstSet = 0; + if (I+1 == E) + DstSet = &Tmp; + else { + DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; + DstSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); + NI != NE; ++NI) + checker->GR_evalDeadSymbols(*DstSet, *Builder, *this, currentStmt, + *NI, SymReaper, tag); + SrcSet = DstSet; + } + } + + if (!Builder->BuildSinks && !Builder->hasGeneratedNode) + Tmp.Add(EntryNode); + } + + bool HasAutoGenerated = false; + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + ExplodedNodeSet Dst; + + // Set the cleaned state. + Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); + + // Visit the statement. + Visit(currentStmt, *I, Dst); + + // Do we need to auto-generate a node? We only need to do this to generate + // a node with a "cleaned" state; CoreEngine will actually handle + // auto-transitions for other cases. + if (Dst.size() == 1 && *Dst.begin() == EntryNode + && !Builder->hasGeneratedNode && !HasAutoGenerated) { + HasAutoGenerated = true; + builder.generateNode(currentStmt, GetState(EntryNode), *I); + } + } + + // NULL out these variables to cleanup. + CleanedState = NULL; + EntryNode = NULL; + + currentStmt = 0; + + Builder = NULL; +} + +void ExprEngine::ProcessInitializer(const CFGInitializer Init, + StmtNodeBuilder &builder) { + // We don't set EntryNode and currentStmt. And we don't clean up state. + const CXXCtorInitializer *BMI = Init.getInitializer(); + + ExplodedNode *pred = builder.getPredecessor(); + + const StackFrameContext *stackFrame = cast<StackFrameContext>(pred->getLocationContext()); + const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); + const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame); + + SVal thisVal = pred->getState()->getSVal(thisReg); + + if (BMI->isAnyMemberInitializer()) { + ExplodedNodeSet Dst; + + // Evaluate the initializer. + Visit(BMI->getInit(), pred, Dst); + + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){ + ExplodedNode *Pred = *I; + const GRState *state = Pred->getState(); + + const FieldDecl *FD = BMI->getAnyMember(); + + SVal FieldLoc = state->getLValue(FD, thisVal); + SVal InitVal = state->getSVal(BMI->getInit()); + state = state->bindLoc(FieldLoc, InitVal); + + // Use a custom node building process. + PostInitializer PP(BMI, stackFrame); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + builder.generateNode(PP, state, Pred); + } + return; + } + + assert(BMI->isBaseInitializer()); + + // Get the base class declaration. + const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); + + // Create the base object region. + SVal baseVal = + getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); + const MemRegion *baseReg = baseVal.getAsRegion(); + assert(baseReg); + Builder = &builder; + ExplodedNodeSet dst; + VisitCXXConstructExpr(ctorExpr, baseReg, pred, dst); +} + +void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, + StmtNodeBuilder &builder) { + Builder = &builder; + + switch (D.getDtorKind()) { + case CFGElement::AutomaticObjectDtor: + ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), builder); + break; + case CFGElement::BaseDtor: + ProcessBaseDtor(cast<CFGBaseDtor>(D), builder); + break; + case CFGElement::MemberDtor: + ProcessMemberDtor(cast<CFGMemberDtor>(D), builder); + break; + case CFGElement::TemporaryDtor: + ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), builder); + break; + default: + llvm_unreachable("Unexpected dtor kind."); + } +} + +void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, + StmtNodeBuilder &builder) { + ExplodedNode *pred = builder.getPredecessor(); + const GRState *state = pred->getState(); + const VarDecl *varDecl = dtor.getVarDecl(); + + QualType varType = varDecl->getType(); + + if (const ReferenceType *refType = varType->getAs<ReferenceType>()) + varType = refType->getPointeeType(); + + const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl(); + assert(recordDecl && "get CXXRecordDecl fail"); + const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor(); + + Loc dest = state->getLValue(varDecl, pred->getLocationContext()); + + ExplodedNodeSet dstSet; + VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(), + dtor.getTriggerStmt(), pred, dstSet); +} + +void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, + StmtNodeBuilder &builder) { +} + +void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, + StmtNodeBuilder &builder) { +} + +void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, + StmtNodeBuilder &builder) { +} + +void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + S->getLocStart(), + "Error evaluating statement"); + + // Expressions to ignore. + if (const Expr *Ex = dyn_cast<Expr>(S)) + S = Ex->IgnoreParens(); + + // FIXME: add metadata to the CFG so that we can disable + // this check when we KNOW that there is no block-level subexpression. + // The motivation is that this check requires a hashtable lookup. + + if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { + Dst.Add(Pred); + return; + } + + switch (S->getStmtClass()) { + // C++ stuff we don't support yet. + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXCatchStmtClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::ExprWithCleanupsClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXThrowExprClass: + case Stmt::CXXTryStmtClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXUuidofExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::UnaryTypeTraitExprClass: + case Stmt::BinaryTypeTraitExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::CXXNoexceptExprClass: + case Stmt::PackExpansionExprClass: + case Stmt::SubstNonTypeTemplateParmPackExprClass: + { + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } + + case Stmt::ParenExprClass: + llvm_unreachable("ParenExprs already handled."); + // Cases that should never be evaluated simply because they shouldn't + // appear in the CFG. + case Stmt::BreakStmtClass: + case Stmt::CaseStmtClass: + case Stmt::CompoundStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::DoStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::LabelStmtClass: + case Stmt::NoStmtClass: + case Stmt::NullStmtClass: + llvm_unreachable("Stmt should not be in analyzer evaluation loop"); + break; + + case Stmt::GNUNullExprClass: { + MakeNode(Dst, S, Pred, GetState(Pred)->BindExpr(S, svalBuilder.makeNull())); + break; + } + + case Stmt::ObjCAtSynchronizedStmtClass: + VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst); + break; + + case Stmt::ObjCPropertyRefExprClass: + VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst); + break; + + // Cases not handled yet; but will handle some day. + case Stmt::DesignatedInitExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCEncodeExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCProtocolExprClass: + case Stmt::ObjCSelectorExprClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::ParenListExprClass: + case Stmt::PredefinedExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::VAArgExprClass: + case Stmt::CUDAKernelCallExprClass: + case Stmt::OpaqueValueExprClass: + // Fall through. + + // Cases we intentionally don't evaluate, since they don't need + // to be explicitly evaluated. + case Stmt::AddrLabelExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::SizeOfPackExprClass: + Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. + break; + + case Stmt::ArraySubscriptExprClass: + VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); + break; + + case Stmt::AsmStmtClass: + VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); + break; + + case Stmt::BlockDeclRefExprClass: { + const BlockDeclRefExpr *BE = cast<BlockDeclRefExpr>(S); + VisitCommonDeclRefExpr(BE, BE->getDecl(), Pred, Dst); + break; + } + + case Stmt::BlockExprClass: + VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); + break; + + case Stmt::BinaryOperatorClass: { + const BinaryOperator* B = cast<BinaryOperator>(S); + if (B->isLogicalOp()) { + VisitLogicalExpr(B, Pred, Dst); + break; + } + else if (B->getOpcode() == BO_Comma) { + const GRState* state = GetState(Pred); + MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); + break; + } + + if (AMgr.shouldEagerlyAssume() && + (B->isRelationalOp() || B->isEqualityOp())) { + ExplodedNodeSet Tmp; + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp); + evalEagerlyAssume(Dst, Tmp, cast<Expr>(S)); + } + else + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + + break; + } + + case Stmt::CallExprClass: { + const CallExpr* C = cast<CallExpr>(S); + VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst); + break; + } + + case Stmt::CXXConstructExprClass: { + const CXXConstructExpr *C = cast<CXXConstructExpr>(S); + // For block-level CXXConstructExpr, we don't have a destination region. + // Let VisitCXXConstructExpr() create one. + VisitCXXConstructExpr(C, 0, Pred, Dst); + break; + } + + case Stmt::CXXMemberCallExprClass: { + const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr>(S); + VisitCXXMemberCallExpr(MCE, Pred, Dst); + break; + } + + case Stmt::CXXOperatorCallExprClass: { + const CXXOperatorCallExpr *C = cast<CXXOperatorCallExpr>(S); + VisitCXXOperatorCallExpr(C, Pred, Dst); + break; + } + + case Stmt::CXXNewExprClass: { + const CXXNewExpr *NE = cast<CXXNewExpr>(S); + VisitCXXNewExpr(NE, Pred, Dst); + break; + } + + case Stmt::CXXDeleteExprClass: { + const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); + VisitCXXDeleteExpr(CDE, Pred, Dst); + break; + } + // FIXME: ChooseExpr is really a constant. We need to fix + // the CFG do not model them as explicit control-flow. + + case Stmt::ChooseExprClass: { // __builtin_choose_expr + const ChooseExpr* C = cast<ChooseExpr>(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + break; + } + + case Stmt::CompoundAssignOperatorClass: + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + break; + + case Stmt::CompoundLiteralExprClass: + VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst); + break; + + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: { // '?' operator + const AbstractConditionalOperator *C + = cast<AbstractConditionalOperator>(S); + VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); + break; + } + + case Stmt::CXXThisExprClass: + VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); + break; + + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DE = cast<DeclRefExpr>(S); + VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); + break; + } + + case Stmt::DeclStmtClass: + VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); + break; + + case Stmt::ForStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast<ForStmt>(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: { + const CastExpr* C = cast<CastExpr>(S); + VisitCast(C, C->getSubExpr(), Pred, Dst); + break; + } + + case Stmt::IfStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast<IfStmt>(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::InitListExprClass: + VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); + break; + + case Stmt::MemberExprClass: + VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); + break; + case Stmt::ObjCIvarRefExprClass: + VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst); + break; + + case Stmt::ObjCForCollectionStmtClass: + VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); + break; + + case Stmt::ObjCMessageExprClass: + VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), Pred, Dst); + break; + + case Stmt::ObjCAtThrowStmtClass: { + // FIXME: This is not complete. We basically treat @throw as + // an abort. + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } + + case Stmt::ReturnStmtClass: + VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst); + break; + + case Stmt::OffsetOfExprClass: + VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst); + break; + + case Stmt::SizeOfAlignOfExprClass: + VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), Pred, Dst); + break; + + case Stmt::StmtExprClass: { + const StmtExpr* SE = cast<StmtExpr>(S); + + if (SE->getSubStmt()->body_empty()) { + // Empty statement expression. + assert(SE->getType() == getContext().VoidTy + && "Empty statement expression must have void type."); + Dst.Add(Pred); + break; + } + + if (Expr* LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { + const GRState* state = GetState(Pred); + MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); + } + else + Dst.Add(Pred); + + break; + } + + case Stmt::StringLiteralClass: { + const GRState* state = GetState(Pred); + SVal V = state->getLValue(cast<StringLiteral>(S)); + MakeNode(Dst, S, Pred, state->BindExpr(S, V)); + return; + } + + case Stmt::SwitchStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast<SwitchStmt>(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast<UnaryOperator>(S); + if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) { + ExplodedNodeSet Tmp; + VisitUnaryOperator(U, Pred, Tmp); + evalEagerlyAssume(Dst, Tmp, U); + } + else + VisitUnaryOperator(U, Pred, Dst); + break; + } + + case Stmt::WhileStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast<WhileStmt>(S)->getConditionVariable(), S, Pred, Dst); + break; + } +} + +//===----------------------------------------------------------------------===// +// Block entrance. (Update counters). +//===----------------------------------------------------------------------===// + +void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes, + GenericNodeBuilder<BlockEntrance> &nodeBuilder){ + + // FIXME: Refactor this into a checker. + const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock(); + ExplodedNode *pred = nodeBuilder.getPredecessor(); + + if (nodeBuilder.getBlockCounter().getNumVisited( + pred->getLocationContext()->getCurrentStackFrame(), + block->getBlockID()) >= AMgr.getMaxVisit()) { + + static int tag = 0; + nodeBuilder.generateNode(pred->getState(), pred, &tag, true); + } +} + +//===----------------------------------------------------------------------===// +// Generic node creation. +//===----------------------------------------------------------------------===// + +ExplodedNode* ExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St, + ProgramPoint::Kind K, const void *tag) { + assert (Builder && "StmtNodeBuilder not present."); + SaveAndRestore<const void*> OldTag(Builder->Tag); + Builder->Tag = tag; + return Builder->MakeNode(Dst, S, Pred, St, K); +} + +//===----------------------------------------------------------------------===// +// Branch processing. +//===----------------------------------------------------------------------===// + +const GRState* ExprEngine::MarkBranch(const GRState* state, + const Stmt* Terminator, + bool branchTaken) { + + switch (Terminator->getStmtClass()) { + default: + return state; + + case Stmt::BinaryOperatorClass: { // '&&' and '||' + + const BinaryOperator* B = cast<BinaryOperator>(Terminator); + BinaryOperator::Opcode Op = B->getOpcode(); + + assert (Op == BO_LAnd || Op == BO_LOr); + + // For &&, if we take the true branch, then the value of the whole + // expression is that of the RHS expression. + // + // For ||, if we take the false branch, then the value of the whole + // expression is that of the RHS expression. + + const Expr* Ex = (Op == BO_LAnd && branchTaken) || + (Op == BO_LOr && !branchTaken) + ? B->getRHS() : B->getLHS(); + + return state->BindExpr(B, UndefinedVal(Ex)); + } + + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: { // ?: + const AbstractConditionalOperator* C + = cast<AbstractConditionalOperator>(Terminator); + + // For ?, if branchTaken == true then the value is either the LHS or + // the condition itself. (GNU extension). + + const Expr* Ex; + + if (branchTaken) + Ex = C->getTrueExpr(); + else + Ex = C->getFalseExpr(); + + return state->BindExpr(C, UndefinedVal(Ex)); + } + + case Stmt::ChooseExprClass: { // ?: + + const ChooseExpr* C = cast<ChooseExpr>(Terminator); + + const Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); + return state->BindExpr(C, UndefinedVal(Ex)); + } + } +} + +/// RecoverCastedSymbol - A helper function for ProcessBranch that is used +/// to try to recover some path-sensitivity for casts of symbolic +/// integers that promote their values (which are currently not tracked well). +/// This function returns the SVal bound to Condition->IgnoreCasts if all the +// cast(s) did was sign-extend the original value. +static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, + const Stmt* Condition, ASTContext& Ctx) { + + const Expr *Ex = dyn_cast<Expr>(Condition); + if (!Ex) + return UnknownVal(); + + uint64_t bits = 0; + bool bitsInit = false; + + while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + QualType T = CE->getType(); + + if (!T->isIntegerType()) + return UnknownVal(); + + uint64_t newBits = Ctx.getTypeSize(T); + if (!bitsInit || newBits < bits) { + bitsInit = true; + bits = newBits; + } + + Ex = CE->getSubExpr(); + } + + // We reached a non-cast. Is it a symbolic value? + QualType T = Ex->getType(); + + if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) + return UnknownVal(); + + return state->getSVal(Ex); +} + +void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term, + BranchNodeBuilder& builder) { + + // Check for NULL conditions; e.g. "for(;;)" + if (!Condition) { + builder.markInfeasible(false); + return; + } + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Condition->getLocStart(), + "Error evaluating branch"); + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + checker->VisitBranchCondition(builder, *this, Condition, tag); + } + + // If the branch condition is undefined, return; + if (!builder.isFeasible(true) && !builder.isFeasible(false)) + return; + + const GRState* PrevState = builder.getState(); + SVal X = PrevState->getSVal(Condition); + + if (X.isUnknown()) { + // Give it a chance to recover from unknown. + if (const Expr *Ex = dyn_cast<Expr>(Condition)) { + if (Ex->getType()->isIntegerType()) { + // Try to recover some path-sensitivity. Right now casts of symbolic + // integers that promote their values are currently not tracked well. + // If 'Condition' is such an expression, try and recover the + // underlying value and use that instead. + SVal recovered = RecoverCastedSymbol(getStateManager(), + builder.getState(), Condition, + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } + } + } + // If the condition is still unknown, give up. + if (X.isUnknown()) { + builder.generateNode(MarkBranch(PrevState, Term, true), true); + builder.generateNode(MarkBranch(PrevState, Term, false), false); + return; + } + } + + DefinedSVal V = cast<DefinedSVal>(X); + + // Process the true branch. + if (builder.isFeasible(true)) { + if (const GRState *state = PrevState->assume(V, true)) + builder.generateNode(MarkBranch(state, Term, true), true); + else + builder.markInfeasible(true); + } + + // Process the false branch. + if (builder.isFeasible(false)) { + if (const GRState *state = PrevState->assume(V, false)) + builder.generateNode(MarkBranch(state, Term, false), false); + else + builder.markInfeasible(false); + } +} + +/// processIndirectGoto - Called by CoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a computed goto jump. +void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { + + const GRState *state = builder.getState(); + SVal V = state->getSVal(builder.getTarget()); + + // Three possibilities: + // + // (1) We know the computed label. + // (2) The label is NULL (or some other constant), or Undefined. + // (3) We have no clue about the label. Dispatch to all targets. + // + + typedef IndirectGotoNodeBuilder::iterator iterator; + + if (isa<loc::GotoLabel>(V)) { + const LabelDecl *L = cast<loc::GotoLabel>(V).getLabel(); + + for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) { + if (I.getLabel() == L) { + builder.generateNode(I, state); + return; + } + } + + assert(false && "No block with label."); + return; + } + + if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) { + // Dispatch to the first target and mark it as a sink. + //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); + // FIXME: add checker visit. + // UndefBranches.insert(N); + return; + } + + // This is really a catch-all. We don't support symbolics yet. + // FIXME: Implement dispatch for symbolic pointers. + + for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) + builder.generateNode(I, state); +} + + +void ExprEngine::VisitGuardedExpr(const Expr* Ex, const Expr* L, + const Expr* R, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + + assert(Ex == currentStmt && + Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)); + + const GRState* state = GetState(Pred); + SVal X = state->getSVal(Ex); + + assert (X.isUndef()); + + const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); + assert(SE); + X = state->getSVal(SE); + + // Make sure that we invalidate the previous binding. + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); +} + +/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path +/// nodes when the control reaches the end of a function. +void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) { + getTF().evalEndPath(*this, builder); + StateMgr.EndPath(builder.getState()); + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + void *tag = I->first; + Checker *checker = I->second; + checker->evalEndPath(builder, tag, *this); + } +} + +/// ProcessSwitch - Called by CoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a switch statement. +void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { + typedef SwitchNodeBuilder::iterator iterator; + const GRState* state = builder.getState(); + const Expr* CondE = builder.getCondition(); + SVal CondV_untested = state->getSVal(CondE); + + if (CondV_untested.isUndef()) { + //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); + // FIXME: add checker + //UndefBranches.insert(N); + + return; + } + DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); + + const GRState *DefaultSt = state; + + iterator I = builder.begin(), EI = builder.end(); + bool defaultIsFeasible = I == EI; + + for ( ; I != EI; ++I) { + const CaseStmt* Case = I.getCase(); + + // Evaluate the LHS of the case value. + Expr::EvalResult V1; + bool b = Case->getLHS()->Evaluate(V1, getContext()); + + // Sanity checks. These go away in Release builds. + assert(b && V1.Val.isInt() && !V1.HasSideEffects + && "Case condition must evaluate to an integer constant."); + (void)b; // silence unused variable warning + assert(V1.Val.getInt().getBitWidth() == + getContext().getTypeSize(CondE->getType())); + + // Get the RHS of the case, if it exists. + Expr::EvalResult V2; + + if (const Expr* E = Case->getRHS()) { + b = E->Evaluate(V2, getContext()); + assert(b && V2.Val.isInt() && !V2.HasSideEffects + && "Case condition must evaluate to an integer constant."); + (void)b; // silence unused variable warning + } + else + V2 = V1; + + // FIXME: Eventually we should replace the logic below with a range + // comparison, rather than concretize the values within the range. + // This should be easy once we have "ranges" for NonLVals. + + do { + nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt())); + DefinedOrUnknownSVal Res = svalBuilder.evalEQ(DefaultSt ? DefaultSt : state, + CondV, CaseVal); + + // Now "assume" that the case matches. + if (const GRState* stateNew = state->assume(Res, true)) { + builder.generateCaseStmtNode(I, stateNew); + + // If CondV evaluates to a constant, then we know that this + // is the *only* case that we can take, so stop evaluating the + // others. + if (isa<nonloc::ConcreteInt>(CondV)) + return; + } + + // Now "assume" that the case doesn't match. Add this state + // to the default state (if it is feasible). + if (DefaultSt) { + if (const GRState *stateNew = DefaultSt->assume(Res, false)) { + defaultIsFeasible = true; + DefaultSt = stateNew; + } + else { + defaultIsFeasible = false; + DefaultSt = NULL; + } + } + + // Concretize the next value in the range. + if (V1.Val.getInt() == V2.Val.getInt()) + break; + + ++V1.Val.getInt(); + assert (V1.Val.getInt() <= V2.Val.getInt()); + + } while (true); + } + + if (!defaultIsFeasible) + return; + + // If we have switch(enum value), the default branch is not + // feasible if all of the enum constants not covered by 'case:' statements + // are not feasible values for the switch condition. + // + // Note that this isn't as accurate as it could be. Even if there isn't + // a case for a particular enum value as long as that enum value isn't + // feasible then it shouldn't be considered for making 'default:' reachable. + const SwitchStmt *SS = builder.getSwitch(); + const Expr *CondExpr = SS->getCond()->IgnoreParenImpCasts(); + if (CondExpr->getType()->getAs<EnumType>()) { + if (SS->isAllEnumCasesCovered()) + return; + } + + builder.generateDefaultCaseNode(DefaultSt); +} + +void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) { + const GRState *state = B.getState()->enterStackFrame(B.getCalleeContext()); + B.generateNode(state); +} + +void ExprEngine::processCallExit(CallExitNodeBuilder &B) { + const GRState *state = B.getState(); + const ExplodedNode *Pred = B.getPredecessor(); + const StackFrameContext *calleeCtx = + cast<StackFrameContext>(Pred->getLocationContext()); + const Stmt *CE = calleeCtx->getCallSite(); + + // If the callee returns an expression, bind its value to CallExpr. + const Stmt *ReturnedExpr = state->get<ReturnExpr>(); + if (ReturnedExpr) { + SVal RetVal = state->getSVal(ReturnedExpr); + state = state->BindExpr(CE, RetVal); + // Clear the return expr GDM. + state = state->remove<ReturnExpr>(); + } + + // Bind the constructed object value to CXXConstructExpr. + if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { + const CXXThisRegion *ThisR = + getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); + + SVal ThisV = state->getSVal(ThisR); + // Always bind the region to the CXXConstructExpr. + state = state->BindExpr(CCE, ThisV); + } + + B.generateNode(state); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: logical operations ('&&', '||'). +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + assert(B->getOpcode() == BO_LAnd || + B->getOpcode() == BO_LOr); + + assert(B==currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); + + const GRState* state = GetState(Pred); + SVal X = state->getSVal(B); + assert(X.isUndef()); + + const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); + assert(Ex); + + if (Ex == B->getRHS()) { + X = state->getSVal(Ex); + + // Handle undefined values. + if (X.isUndef()) { + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + return; + } + + DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X); + + // We took the RHS. Because the value of the '&&' or '||' expression must + // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 + // or 1. Alternatively, we could take a lazy approach, and calculate this + // value later when necessary. We don't have the machinery in place for + // this right now, and since most logical expressions are used for branches, + // the payoff is not likely to be large. Instead, we do eager evaluation. + if (const GRState *newState = state->assume(XD, true)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType()))); + + if (const GRState *newState = state->assume(XD, false)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType()))); + } + else { + // We took the LHS expression. Depending on whether we are '&&' or + // '||' we know what the value of the expression is via properties of + // the short-circuiting. + X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, + B->getType()); + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Loads and stores. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + ExplodedNodeSet Tmp; + + CanQualType T = getContext().getCanonicalType(BE->getType()); + SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, + Pred->getLocationContext()); + + MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), + ProgramPoint::PostLValueKind); + + // Post-visit the BlockExpr. + CheckerVisit(BE, Dst, Tmp, PostVisitStmtCallback); +} + +void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const GRState *state = GetState(Pred); + + if (const VarDecl* VD = dyn_cast<VarDecl>(D)) { + assert(Ex->isLValue()); + SVal V = state->getLValue(VD, Pred->getLocationContext()); + + // For references, the 'lvalue' is the pointer address stored in the + // reference region. + if (VD->getType()->isReferenceType()) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } + + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), + ProgramPoint::PostLValueKind); + return; + } + if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) { + assert(!Ex->isLValue()); + SVal V = svalBuilder.makeIntVal(ED->getInitVal()); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); + return; + } + if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) { + SVal V = svalBuilder.getFunctionPointer(FD); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), + ProgramPoint::PostLValueKind); + return; + } + assert (false && + "ValueDecl support for this ValueDecl not implemented."); +} + +/// VisitArraySubscriptExpr - Transfer function for array accesses +void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr* A, + ExplodedNode* Pred, + ExplodedNodeSet& Dst){ + + const Expr* Base = A->getBase()->IgnoreParens(); + const Expr* Idx = A->getIdx()->IgnoreParens(); + + // Evaluate the base. + ExplodedNodeSet Tmp; + Visit(Base, Pred, Tmp); + + for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { + ExplodedNodeSet Tmp2; + Visit(Idx, *I1, Tmp2); // Evaluate the index. + ExplodedNodeSet Tmp3; + CheckerVisit(A, Tmp3, Tmp2, PreVisitStmtCallback); + + for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { + const GRState* state = GetState(*I2); + SVal V = state->getLValue(A->getType(), state->getSVal(Idx), + state->getSVal(Base)); + assert(A->isLValue()); + MakeNode(Dst, A, *I2, state->BindExpr(A, V), ProgramPoint::PostLValueKind); + } + } +} + +/// VisitMemberExpr - Transfer function for member expressions. +void ExprEngine::VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + Expr *baseExpr = M->getBase()->IgnoreParens(); + ExplodedNodeSet dstBase; + Visit(baseExpr, Pred, dstBase); + + FieldDecl *field = dyn_cast<FieldDecl>(M->getMemberDecl()); + if (!field) // FIXME: skipping member expressions for non-fields + return; + + for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); + I != E; ++I) { + const GRState* state = GetState(*I); + SVal baseExprVal = state->getSVal(baseExpr); + if (isa<nonloc::LazyCompoundVal>(baseExprVal) || + isa<nonloc::CompoundVal>(baseExprVal) || + // FIXME: This can originate by conjuring a symbol for an unknown + // temporary struct object, see test/Analysis/fields.c: + // (p = getit()).x + isa<nonloc::SymbolVal>(baseExprVal)) { + MakeNode(Dst, M, *I, state->BindExpr(M, UnknownVal())); + continue; + } + + // FIXME: Should we insert some assumption logic in here to determine + // if "Base" is a valid piece of memory? Before we put this assumption + // later when using FieldOffset lvals (which we no longer have). + + // For all other cases, compute an lvalue. + SVal L = state->getLValue(field, baseExprVal); + if (M->isLValue()) + MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); + else + evalLoad(Dst, M, *I, state, L); + } +} + +/// evalBind - Handle the semantics of binding a value to a specific location. +/// This method is used by evalStore and (soon) VisitDeclStmt, and others. +void ExprEngine::evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE, + ExplodedNode* Pred, const GRState* state, + SVal location, SVal Val, bool atDeclInit) { + + + // Do a previsit of the bind. + ExplodedNodeSet CheckedSet, Src; + Src.Add(Pred); + CheckerVisitBind(StoreE, CheckedSet, Src, location, Val, true); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I!=E; ++I) { + + if (Pred != *I) + state = GetState(*I); + + const GRState* newState = 0; + + if (atDeclInit) { + const VarRegion *VR = + cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion()); + + newState = state->bindDecl(VR, Val); + } + else { + if (location.isUnknown()) { + // We know that the new state will be the same as the old state since + // the location of the binding is "unknown". Consequently, there + // is no reason to just create a new node. + newState = state; + } + else { + // We are binding to a value other than 'unknown'. Perform the binding + // using the StoreManager. + newState = state->bindLoc(cast<Loc>(location), Val); + } + } + + // The next thing to do is check if the TransferFuncs object wants to + // update the state based on the new binding. If the GRTransferFunc object + // doesn't do anything, just auto-propagate the current state. + + // NOTE: We use 'AssignE' for the location of the PostStore if 'AssignE' + // is non-NULL. Checkers typically care about + + StmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, + true); + + getTF().evalBind(BuilderRef, location, Val); + } +} + +/// evalStore - Handle the semantics of a store via an assignment. +/// @param Dst The node set to store generated state nodes +/// @param AssignE The assignment expression if the store happens in an +/// assignment. +/// @param LocatioinE The location expression that is stored to. +/// @param state The current simulation state +/// @param location The location to store the value +/// @param Val The value to be stored +void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE, + const Expr* LocationE, + ExplodedNode* Pred, + const GRState* state, SVal location, SVal Val, + const void *tag) { + + assert(Builder && "StmtNodeBuilder must be defined."); + + // Proceed with the store. We use AssignE as the anchor for the PostStore + // ProgramPoint if it is non-NULL, and LocationE otherwise. + const Expr *StoreE = AssignE ? AssignE : LocationE; + + if (isa<loc::ObjCPropRef>(location)) { + loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); + ExplodedNodeSet src = Pred; + return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(), + StoreE, Val), src, Dst); + } + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + evalLocation(Tmp, LocationE, Pred, state, location, tag, false); + + if (Tmp.empty()) + return; + + assert(!location.isUndef()); + + SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind, + ProgramPoint::PostStoreKind); + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + evalBind(Dst, StoreE, *NI, GetState(*NI), location, Val); +} + +void ExprEngine::evalLoad(ExplodedNodeSet& Dst, const Expr *Ex, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); + + if (isa<loc::ObjCPropRef>(location)) { + loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); + ExplodedNodeSet src = Pred; + return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex), + src, Dst); + } + + // Are we loading from a region? This actually results in two loads; one + // to fetch the address of the referenced value and one to fetch the + // referenced value. + if (const TypedRegion *TR = + dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + + QualType ValTy = TR->getValueType(); + if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { + static int loadReferenceTag = 0; + ExplodedNodeSet Tmp; + evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, + getContext().getPointerType(RT->getPointeeType())); + + // Perform the load from the referenced value. + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { + state = GetState(*I); + location = state->getSVal(Ex); + evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); + } + return; + } + } + + evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); +} + +void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + evalLocation(Tmp, Ex, Pred, state, location, tag, true); + + if (Tmp.empty()) + return; + + assert(!location.isUndef()); + + SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); + + // Proceed with the load. + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + state = GetState(*NI); + + if (location.isUnknown()) { + // This is important. We must nuke the old binding. + MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), + ProgramPoint::PostLoadKind, tag); + } + else { + if (LoadTy.isNull()) + LoadTy = Ex->getType(); + SVal V = state->getSVal(cast<Loc>(location), LoadTy); + MakeNode(Dst, Ex, *NI, state->bindExprAndLocation(Ex, location, V), + ProgramPoint::PostLoadKind, tag); + } + } +} + +void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, bool isLoad) { + // Early checks for performance reason. + if (location.isUnknown() || Checkers.empty()) { + Dst.Add(Pred); + return; + } + + ExplodedNodeSet Src, Tmp; + Src.Add(Pred); + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) { + // Use the 'state' argument only when the predecessor node is the + // same as Pred. This allows us to catch updates to the state. + checker->GR_visitLocation(*CurrSet, *Builder, *this, S, *NI, + *NI == Pred ? state : GetState(*NI), + location, tag, isLoad); + } + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } +} + +bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, + ExplodedNode *Pred) { + const GRState *state = GetState(Pred); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + // Check if the function definition is in the same translation unit. + if (FD->hasBody(FD)) { + const StackFrameContext *stackFrame = + AMgr.getStackFrame(AMgr.getAnalysisContext(FD), + Pred->getLocationContext(), + CE, Builder->getBlock(), Builder->getIndex()); + // Now we have the definition of the callee, create a CallEnter node. + CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); + + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); + Dst.Add(N); + return true; + } + + // Check if we can find the function definition in other translation units. + if (AMgr.hasIndexer()) { + AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD); + if (C == 0) + return false; + const StackFrameContext *stackFrame = + AMgr.getStackFrame(C, Pred->getLocationContext(), + CE, Builder->getBlock(), Builder->getIndex()); + CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); + Dst.Add(N); + return true; + } + + return false; +} + +void ExprEngine::VisitCall(const CallExpr* CE, ExplodedNode* Pred, + CallExpr::const_arg_iterator AI, + CallExpr::const_arg_iterator AE, + ExplodedNodeSet& Dst) { + + // Determine the type of function we're calling (if available). + const FunctionProtoType *Proto = NULL; + QualType FnType = CE->getCallee()->IgnoreParens()->getType(); + if (const PointerType *FnTypePtr = FnType->getAs<PointerType>()) + Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>(); + + // Evaluate the arguments. + ExplodedNodeSet ArgsEvaluated; + evalArguments(CE->arg_begin(), CE->arg_end(), Proto, Pred, ArgsEvaluated); + + // Now process the call itself. + ExplodedNodeSet DstTmp; + const Expr* Callee = CE->getCallee()->IgnoreParens(); + + for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), + NE=ArgsEvaluated.end(); NI != NE; ++NI) { + // Evaluate the callee. + ExplodedNodeSet DstTmp2; + Visit(Callee, *NI, DstTmp2); + // Perform the previsit of the CallExpr, storing the results in DstTmp. + CheckerVisit(CE, DstTmp, DstTmp2, PreVisitStmtCallback); + } + + // Finally, evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet DstTmp3; + + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); + DI != DE; ++DI) { + + const GRState* state = GetState(*DI); + SVal L = state->getSVal(Callee); + + // FIXME: Add support for symbolic function calls (calls involving + // function pointer values that are symbolic). + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + ExplodedNodeSet DstChecker; + + // If the callee is processed by a checker, skip the rest logic. + if (CheckerEvalCall(CE, DstChecker, *DI)) + DstTmp3.insert(DstChecker); + else if (AMgr.shouldInlineCall() && InlineCall(Dst, CE, *DI)) { + // Callee is inlined. We shouldn't do post call checking. + return; + } + else { + for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), + DE_Checker = DstChecker.end(); + DI_Checker != DE_Checker; ++DI_Checker) { + + // Dispatch to the plug-in transfer function. + unsigned oldSize = DstTmp3.size(); + SaveOr OldHasGen(Builder->hasGeneratedNode); + Pred = *DI_Checker; + + // Dispatch to transfer function logic to handle the call itself. + // FIXME: Allow us to chain together transfer functions. + assert(Builder && "StmtNodeBuilder must be defined."); + getTF().evalCall(DstTmp3, *this, *Builder, CE, L, Pred); + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && DstTmp3.size() == oldSize && + !Builder->hasGeneratedNode) + MakeNode(DstTmp3, CE, Pred, state); + } + } + } + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + CheckerVisit(CE, Dst, DstTmp3, PostVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C dot-syntax to access a property. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet dstBase; + + // Visit the receiver (if any). + if (Ex->isObjectReceiver()) + Visit(Ex->getBase(), Pred, dstBase); + else + dstBase = Pred; + + ExplodedNodeSet dstPropRef; + + // Using the base, compute the lvalue of the instance variable. + for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); + I!=E; ++I) { + ExplodedNode *nodeBase = *I; + const GRState *state = GetState(nodeBase); + MakeNode(dstPropRef, Ex, *I, state->BindExpr(Ex, loc::ObjCPropRef(Ex))); + } + + Dst.insert(dstPropRef); +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +static std::pair<const void*,const void*> EagerlyAssumeTag + = std::pair<const void*,const void*>(&EagerlyAssumeTag,static_cast<void*>(0)); + +void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + const Expr *Ex) { + for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { + ExplodedNode *Pred = *I; + + // Test if the previous node was as the same expression. This can happen + // when the expression fails to evaluate to anything meaningful and + // (as an optimization) we don't generate a node. + ProgramPoint P = Pred->getLocation(); + if (!isa<PostStmt>(P) || cast<PostStmt>(P).getStmt() != Ex) { + Dst.Add(Pred); + continue; + } + + const GRState* state = GetState(Pred); + SVal V = state->getSVal(Ex); + if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) { + // First assume that the condition is true. + if (const GRState *stateTrue = state->assume(*SEV, true)) { + stateTrue = stateTrue->BindExpr(Ex, + svalBuilder.makeIntVal(1U, Ex->getType())); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, + &EagerlyAssumeTag, Pred->getLocationContext()), + stateTrue, Pred)); + } + + // Next, assume that the condition is false. + if (const GRState *stateFalse = state->assume(*SEV, false)) { + stateFalse = stateFalse->BindExpr(Ex, + svalBuilder.makeIntVal(0U, Ex->getType())); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag, + Pred->getLocationContext()), + stateFalse, Pred)); + } + } + else + Dst.Add(Pred); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C @synchronized. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + // The mutex expression is a CFGElement, so we don't need to explicitly + // visit it since it will already be processed. + + // Pre-visit the ObjCAtSynchronizedStmt. + ExplodedNodeSet Tmp; + Tmp.Add(Pred); + CheckerVisit(S, Dst, Tmp, PreVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* Ex, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + // Visit the base expression, which is needed for computing the lvalue + // of the ivar. + ExplodedNodeSet dstBase; + const Expr *baseExpr = Ex->getBase(); + Visit(baseExpr, Pred, dstBase); + + ExplodedNodeSet dstIvar; + + // Using the base, compute the lvalue of the instance variable. + for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); + I!=E; ++I) { + ExplodedNode *nodeBase = *I; + const GRState *state = GetState(nodeBase); + SVal baseVal = state->getSVal(baseExpr); + SVal location = state->getLValue(Ex->getDecl(), baseVal); + MakeNode(dstIvar, Ex, *I, state->BindExpr(Ex, location)); + } + + // Perform the post-condition check of the ObjCIvarRefExpr and store + // the created nodes in 'Dst'. + CheckerVisit(Ex, Dst, dstIvar, PostVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C fast enumeration 'for' statements. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt* S, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + + // ObjCForCollectionStmts are processed in two places. This method + // handles the case where an ObjCForCollectionStmt* occurs as one of the + // statements within a basic block. This transfer function does two things: + // + // (1) binds the next container value to 'element'. This creates a new + // node in the ExplodedGraph. + // + // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating + // whether or not the container has any more elements. This value + // will be tested in ProcessBranch. We need to explicitly bind + // this value because a container can contain nil elements. + // + // FIXME: Eventually this logic should actually do dispatches to + // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). + // This will require simulating a temporary NSFastEnumerationState, either + // through an SVal or through the use of MemRegions. This value can + // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop + // terminates we reclaim the temporary (it goes out of scope) and we + // we can test if the SVal is 0 or if the MemRegion is null (depending + // on what approach we take). + // + // For now: simulate (1) by assigning either a symbol or nil if the + // container is empty. Thus this transfer function will by default + // result in state splitting. + + const Stmt* elem = S->getElement(); + SVal ElementV; + + if (const DeclStmt* DS = dyn_cast<DeclStmt>(elem)) { + const VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl()); + assert (ElemD->getInit() == 0); + ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); + VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); + return; + } + + ExplodedNodeSet Tmp; + Visit(cast<Expr>(elem), Pred, Tmp); + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem)); + } +} + +void ExprEngine::VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S, + ExplodedNode* Pred, ExplodedNodeSet& Dst, + SVal ElementV) { + + // Check if the location we are writing back to is a null pointer. + const Stmt* elem = S->getElement(); + ExplodedNodeSet Tmp; + evalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); + + if (Tmp.empty()) + return; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + Pred = *NI; + const GRState *state = GetState(Pred); + + // Handle the case where the container still has elements. + SVal TrueV = svalBuilder.makeTruthVal(1); + const GRState *hasElems = state->BindExpr(S, TrueV); + + // Handle the case where the container has no elements. + SVal FalseV = svalBuilder.makeTruthVal(0); + const GRState *noElems = state->BindExpr(S, FalseV); + + if (loc::MemRegionVal* MV = dyn_cast<loc::MemRegionVal>(&ElementV)) + if (const TypedRegion* R = dyn_cast<TypedRegion>(MV->getRegion())) { + // FIXME: The proper thing to do is to really iterate over the + // container. We will do this with dispatch logic to the store. + // For now, just 'conjure' up a symbolic value. + QualType T = R->getValueType(); + assert(Loc::isLocType(T)); + unsigned Count = Builder->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + SVal V = svalBuilder.makeLoc(Sym); + hasElems = hasElems->bindLoc(ElementV, V); + + // Bind the location to 'nil' on the false branch. + SVal nilV = svalBuilder.makeIntVal(0, T); + noElems = noElems->bindLoc(ElementV, nilV); + } + + // Create the new nodes. + MakeNode(Dst, S, Pred, hasElems); + MakeNode(Dst, S, Pred, noElems); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C message expressions. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCMsgWLItem { +public: + ObjCMessageExpr::const_arg_iterator I; + ExplodedNode *N; + + ObjCMsgWLItem(const ObjCMessageExpr::const_arg_iterator &i, ExplodedNode *n) + : I(i), N(n) {} +}; +} // end anonymous namespace + +void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, + ExplodedNode* Pred, + ExplodedNodeSet& Dst){ + + // Create a worklist to process both the arguments. + llvm::SmallVector<ObjCMsgWLItem, 20> WL; + + // But first evaluate the receiver (if any). + ObjCMessageExpr::const_arg_iterator AI = ME->arg_begin(), AE = ME->arg_end(); + if (const Expr *Receiver = ME->getInstanceReceiver()) { + ExplodedNodeSet Tmp; + Visit(Receiver, Pred, Tmp); + + if (Tmp.empty()) + return; + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) + WL.push_back(ObjCMsgWLItem(AI, *I)); + } + else + WL.push_back(ObjCMsgWLItem(AI, Pred)); + + // Evaluate the arguments. + ExplodedNodeSet ArgsEvaluated; + while (!WL.empty()) { + ObjCMsgWLItem Item = WL.back(); + WL.pop_back(); + + if (Item.I == AE) { + ArgsEvaluated.insert(Item.N); + continue; + } + + // Evaluate the subexpression. + ExplodedNodeSet Tmp; + + // FIXME: [Objective-C++] handle arguments that are references + Visit(*Item.I, Item.N, Tmp); + + // Enqueue evaluating the next argument on the worklist. + ++(Item.I); + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + WL.push_back(ObjCMsgWLItem(Item.I, *NI)); + } + + // Now that the arguments are processed, handle the ObjC message. + VisitObjCMessage(ME, ArgsEvaluated, Dst); +} + +void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, + ExplodedNodeSet &Src, ExplodedNodeSet& Dst) { + + // Handle the previsits checks. + ExplodedNodeSet DstPrevisit; + CheckerVisitObjCMessage(msg, DstPrevisit, Src, /*isPreVisit=*/true); + + // Proceed with evaluate the message expression. + ExplodedNodeSet dstEval; + + for (ExplodedNodeSet::iterator DI = DstPrevisit.begin(), + DE = DstPrevisit.end(); DI != DE; ++DI) { + + ExplodedNode *Pred = *DI; + bool RaisesException = false; + unsigned oldSize = dstEval.size(); + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->hasGeneratedNode); + + if (const Expr *Receiver = msg.getInstanceReceiver()) { + const GRState *state = GetState(Pred); + + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = + cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); + + const GRState *notNilState, *nilState; + llvm::tie(notNilState, nilState) = state->assume(receiverVal); + + // There are three cases: can be nil or non-nil, must be nil, must be + // non-nil. We handle must be nil, and merge the rest two into non-nil. + if (nilState && !notNilState) { + CheckerEvalNilReceiver(msg, dstEval, nilState, Pred); + continue; + } + + // Check if the "raise" message was sent. + assert(notNilState); + if (msg.getSelector() == RaiseSel) + RaisesException = true; + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + evalObjCMessage(dstEval, msg, Pred, notNilState); + } + else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { + IdentifierInfo* ClsName = Iface->getIdentifier(); + Selector S = msg.getSelector(); + + // Check for special instance methods. + if (!NSExceptionII) { + ASTContext& Ctx = getContext(); + NSExceptionII = &Ctx.Idents.get("NSException"); + } + + if (ClsName == NSExceptionII) { + enum { NUM_RAISE_SELECTORS = 2 }; + + // Lazily create a cache of the selectors. + if (!NSExceptionInstanceRaiseSelectors) { + ASTContext& Ctx = getContext(); + NSExceptionInstanceRaiseSelectors = + new Selector[NUM_RAISE_SELECTORS]; + llvm::SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; + unsigned idx = 0; + + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + + // raise:format::arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + } + + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; + break; + } + } + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + evalObjCMessage(dstEval, msg, Pred, Builder->GetState(Pred)); + } + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && dstEval.size() == oldSize && + !Builder->hasGeneratedNode) + MakeNode(dstEval, msg.getOriginExpr(), Pred, GetState(Pred)); + } + + // Finally, perform the post-condition check of the ObjCMessageExpr and store + // the created nodes in 'Dst'. + CheckerVisitObjCMessage(msg, Dst, dstEval, /*isPreVisit=*/false); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Miscellaneous statements. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + + ExplodedNodeSet S1; + Visit(Ex, Pred, S1); + ExplodedNodeSet S2; + CheckerVisit(CastE, S2, S1, PreVisitStmtCallback); + + if (CastE->getCastKind() == CK_LValueToRValue || + CastE->getCastKind() == CK_GetObjCProperty) { + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I!=E; ++I) { + ExplodedNode *subExprNode = *I; + const GRState *state = GetState(subExprNode); + evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex)); + } + return; + } + + // All other casts. + QualType T = CastE->getType(); + QualType ExTy = Ex->getType(); + + if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) + T = ExCast->getTypeAsWritten(); + +#if 0 + // If we are evaluating the cast in an lvalue context, we implicitly want + // the cast to evaluate to a location. + if (asLValue) { + ASTContext &Ctx = getContext(); + T = Ctx.getPointerType(Ctx.getCanonicalType(T)); + ExTy = Ctx.getPointerType(Ctx.getCanonicalType(ExTy)); + } +#endif + + switch (CastE->getCastKind()) { + case CK_ToVoid: + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) + Dst.Add(*I); + return; + + case CK_LValueToRValue: + case CK_NoOp: + case CK_FunctionToPointerDecay: + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + // Copy the SVal of Ex to CastE. + ExplodedNode *N = *I; + const GRState *state = GetState(N); + SVal V = state->getSVal(Ex); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, N, state); + } + return; + + case CK_GetObjCProperty: + case CK_Dependent: + case CK_ArrayToPointerDecay: + case CK_BitCast: + case CK_LValueBitCast: + case CK_IntegralCast: + case CK_NullToPointer: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_PointerToBoolean: + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_FloatingRealToComplex: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + + case CK_ObjCObjectLValueCast: { + // Delegate to SValBuilder to process. + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + ExplodedNode* N = *I; + const GRState* state = GetState(N); + SVal V = state->getSVal(Ex); + V = svalBuilder.evalCast(V, T, ExTy); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, N, state); + } + return; + } + + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + // For DerivedToBase cast, delegate to the store manager. + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + ExplodedNode *node = *I; + const GRState *state = GetState(node); + SVal val = state->getSVal(Ex); + val = getStoreManager().evalDerivedToBase(val, T); + state = state->BindExpr(CastE, val); + MakeNode(Dst, CastE, node, state); + } + return; + + // Various C++ casts that are not handled yet. + case CK_Dynamic: + case CK_ToUnion: + case CK_BaseToDerived: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_UserDefinedConversion: + case CK_ConstructorConversion: + case CK_VectorSplat: + case CK_MemberPointerToBoolean: { + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, CastE, Pred, GetState(Pred)); + return; + } + } +} + +void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + const InitListExpr* ILE + = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); + ExplodedNodeSet Tmp; + Visit(ILE, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { + const GRState* state = GetState(*I); + SVal ILV = state->getSVal(ILE); + const LocationContext *LC = (*I)->getLocationContext(); + state = state->bindCompoundLiteral(CL, LC, ILV); + + if (CL->isLValue()) { + MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); + } + else + MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); + } +} + +void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, + ExplodedNodeSet& Dst) { + + // The CFG has one DeclStmt per Decl. + const Decl* D = *DS->decl_begin(); + + if (!D || !isa<VarDecl>(D)) + return; + + const VarDecl* VD = dyn_cast<VarDecl>(D); + const Expr* InitEx = VD->getInit(); + + // FIXME: static variables may have an initializer, but the second + // time a function is called those values may not be current. + ExplodedNodeSet Tmp; + + if (InitEx) { + if (VD->getType()->isReferenceType() && !InitEx->isLValue()) { + // If the initializer is C++ record type, it should already has a + // temp object. + if (!InitEx->getType()->isRecordType()) + CreateCXXTemporaryObject(InitEx, Pred, Tmp); + else + Tmp.Add(Pred); + } else + Visit(InitEx, Pred, Tmp); + } else + Tmp.Add(Pred); + + ExplodedNodeSet Tmp2; + CheckerVisit(DS, Tmp2, Tmp, PreVisitStmtCallback); + + for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { + ExplodedNode *N = *I; + const GRState *state = GetState(N); + + // Decls without InitExpr are not initialized explicitly. + const LocationContext *LC = N->getLocationContext(); + + if (InitEx) { + SVal InitVal = state->getSVal(InitEx); + + // We bound the temp obj region to the CXXConstructExpr. Now recover + // the lazy compound value when the variable is not a reference. + if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() && + !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ + InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); + assert(isa<nonloc::LazyCompoundVal>(InitVal)); + } + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if ((InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) && + !VD->getType()->isReferenceType()) { + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + evalBind(Dst, DS, *I, state, + loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); + } + else { + state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); + MakeNode(Dst, DS, *I, state); + } + } +} + +void ExprEngine::VisitCondInit(const VarDecl *VD, const Stmt *S, + ExplodedNode *Pred, ExplodedNodeSet& Dst) { + + const Expr* InitEx = VD->getInit(); + ExplodedNodeSet Tmp; + Visit(InitEx, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + ExplodedNode *N = *I; + const GRState *state = GetState(N); + + const LocationContext *LC = N->getLocationContext(); + SVal InitVal = state->getSVal(InitEx); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) { + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + evalBind(Dst, S, N, state, + loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); + } +} + +namespace { + // This class is used by VisitInitListExpr as an item in a worklist + // for processing the values contained in an InitListExpr. +class InitListWLItem { +public: + llvm::ImmutableList<SVal> Vals; + ExplodedNode* N; + InitListExpr::const_reverse_iterator Itr; + + InitListWLItem(ExplodedNode* n, llvm::ImmutableList<SVal> vals, + InitListExpr::const_reverse_iterator itr) + : Vals(vals), N(n), Itr(itr) {} +}; +} + + +void ExprEngine::VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + const GRState* state = GetState(Pred); + QualType T = getContext().getCanonicalType(E->getType()); + unsigned NumInitElements = E->getNumInits(); + + if (T->isArrayType() || T->isRecordType() || T->isVectorType()) { + llvm::ImmutableList<SVal> StartVals = getBasicVals().getEmptySValList(); + + // Handle base case where the initializer has no elements. + // e.g: static int* myArray[] = {}; + if (NumInitElements == 0) { + SVal V = svalBuilder.makeCompoundVal(T, StartVals); + MakeNode(Dst, E, Pred, state->BindExpr(E, V)); + return; + } + + // Create a worklist to process the initializers. + llvm::SmallVector<InitListWLItem, 10> WorkList; + WorkList.reserve(NumInitElements); + WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); + InitListExpr::const_reverse_iterator ItrEnd = E->rend(); + assert(!(E->rbegin() == E->rend())); + + // Process the worklist until it is empty. + while (!WorkList.empty()) { + InitListWLItem X = WorkList.back(); + WorkList.pop_back(); + + ExplodedNodeSet Tmp; + Visit(*X.Itr, X.N, Tmp); + + InitListExpr::const_reverse_iterator NewItr = X.Itr + 1; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { + // Get the last initializer value. + state = GetState(*NI); + SVal InitV = state->getSVal(cast<Expr>(*X.Itr)); + + // Construct the new list of values by prepending the new value to + // the already constructed list. + llvm::ImmutableList<SVal> NewVals = + getBasicVals().consVals(InitV, X.Vals); + + if (NewItr == ItrEnd) { + // Now we have a list holding all init values. Make CompoundValData. + SVal V = svalBuilder.makeCompoundVal(T, NewVals); + + // Make final state and node. + MakeNode(Dst, E, *NI, state->BindExpr(E, V)); + } + else { + // Still some initializer values to go. Push them onto the worklist. + WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); + } + } + } + + return; + } + + if (Loc::isLocType(T) || T->isIntegerType()) { + assert (E->getNumInits() == 1); + ExplodedNodeSet Tmp; + const Expr* Init = E->getInit(0); + Visit(Init, Pred, Tmp); + for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { + state = GetState(*I); + MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init))); + } + return; + } + + assert(0 && "unprocessed InitListExpr type"); +} + +/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). +void ExprEngine::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr* Ex, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + QualType T = Ex->getTypeOfArgument(); + CharUnits amt; + + if (Ex->isSizeOf()) { + if (T == getContext().VoidTy) { + // sizeof(void) == 1 byte. + amt = CharUnits::One(); + } + else if (!T->isConstantSizeType()) { + assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); + + // FIXME: Add support for VLA type arguments, not just VLA expressions. + // When that happens, we should probably refactor VLASizeChecker's code. + if (Ex->isArgumentType()) { + Dst.Add(Pred); + return; + } + + // Get the size by getting the extent of the sub-expression. + // First, visit the sub-expression to find its region. + const Expr *Arg = Ex->getArgumentExpr(); + ExplodedNodeSet Tmp; + Visit(Arg, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + const MemRegion *MR = state->getSVal(Arg).getAsRegion(); + + // If the subexpression can't be resolved to a region, we don't know + // anything about its size. Just leave the state as is and continue. + if (!MR) { + Dst.Add(*I); + continue; + } + + // The result is the extent of the VLA. + SVal Extent = cast<SubRegion>(MR)->getExtent(svalBuilder); + MakeNode(Dst, Ex, *I, state->BindExpr(Ex, Extent)); + } + + return; + } + else if (T->getAs<ObjCObjectType>()) { + // Some code tries to take the sizeof an ObjCObjectType, relying that + // the compiler has laid out its representation. Just report Unknown + // for these. + Dst.Add(Pred); + return; + } + else { + // All other cases. + amt = getContext().getTypeSizeInChars(T); + } + } + else // Get alignment of the type. + amt = getContext().getTypeAlignInChars(T); + + MakeNode(Dst, Ex, Pred, + GetState(Pred)->BindExpr(Ex, + svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType()))); +} + +void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr* OOE, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + Expr::EvalResult Res; + if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { + const APSInt &IV = Res.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); + assert(OOE->getType()->isIntegerType()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); + SVal X = svalBuilder.makeIntVal(IV); + MakeNode(Dst, OOE, Pred, GetState(Pred)->BindExpr(OOE, X)); + return; + } + // FIXME: Handle the case where __builtin_offsetof is not a constant. + Dst.Add(Pred); +} + +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + switch (U->getOpcode()) { + + default: + break; + + case UO_Real: { + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UO_Real is an identity operation. + assert (U->getType() == Ex->getType()); + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UO_Imag: { + + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UO_Imag returns 0. + const GRState* state = GetState(*I); + SVal X = svalBuilder.makeZeroVal(Ex->getType()); + MakeNode(Dst, U, *I, state->BindExpr(U, X)); + } + + return; + } + + case UO_Plus: + assert(!U->isLValue()); + // FALL-THROUGH. + case UO_Deref: + case UO_AddrOf: + case UO_Extension: { + + // Unary "+" is a no-op, similar to a parentheses. We still have places + // where it may be a block-level expression, so we need to + // generate an extra node that just propagates the value of the + // subexpression. + + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UO_LNot: + case UO_Minus: + case UO_Not: { + assert (!U->isLValue()); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + + // Get the value of the subexpression. + SVal V = state->getSVal(Ex); + + if (V.isUnknownOrUndef()) { + MakeNode(Dst, U, *I, state->BindExpr(U, V)); + continue; + } + +// QualType DstT = getContext().getCanonicalType(U->getType()); +// QualType SrcT = getContext().getCanonicalType(Ex->getType()); +// +// if (DstT != SrcT) // Perform promotions. +// V = evalCast(V, DstT); +// +// if (V.isUnknownOrUndef()) { +// MakeNode(Dst, U, *I, BindExpr(St, U, V)); +// continue; +// } + + switch (U->getOpcode()) { + default: + assert(false && "Invalid Opcode."); + break; + + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, evalComplement(cast<NonLoc>(V))); + break; + + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, evalMinus(cast<NonLoc>(V))); + break; + + case UO_LNot: + + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + + if (isa<Loc>(V)) { + Loc X = svalBuilder.makeNull(); + Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, + U->getType()); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, + U->getType()); + } + + state = state->BindExpr(U, Result); + + break; + } + + MakeNode(Dst, U, *I, state); + } + + return; + } + } + + // Handle ++ and -- (both pre- and post-increment). + assert (U->isIncrementDecrementOp()); + ExplodedNodeSet Tmp; + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + + const GRState* state = GetState(*I); + SVal loc = state->getSVal(Ex); + + // Perform a load. + ExplodedNodeSet Tmp2; + evalLoad(Tmp2, Ex, *I, state, loc); + + for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { + + state = GetState(*I2); + SVal V2_untested = state->getSVal(Ex); + + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); + continue; + } + DefinedSVal V2 = cast<DefinedSVal>(V2_untested); + + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add + : BO_Sub; + + // If the UnaryOperator has non-location type, use its type to create the + // constant value. If the UnaryOperator has location type, create the + // constant with int type and pointer width. + SVal RHS; + + if (U->getType()->isAnyPointerType()) + RHS = svalBuilder.makeArrayIndex(1); + else + RHS = svalBuilder.makeIntVal(1, U->getType()); + + SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ + DefinedOrUnknownSVal SymVal = + svalBuilder.getConjuredSymbolVal(NULL, Ex, + Builder->getCurrentBlockCount()); + Result = SymVal; + + // If the value is a location, ++/-- should always preserve + // non-nullness. Check if the original value was non-null, and if so + // propagate that constraint. + if (Loc::isLocType(U->getType())) { + DefinedOrUnknownSVal Constraint = + svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); + + if (!state->assume(Constraint, true)) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = svalBuilder.evalEQ(state, SymVal, + svalBuilder.makeZeroVal(U->getType())); + + + state = state->assume(Constraint, false); + assert(state); + } + } + } + + // Since the lvalue-to-rvalue conversion is explicit in the AST, + // we bind an l-value if the operator is prefix and an lvalue (in C++). + if (U->isLValue()) + state = state->BindExpr(U, loc); + else + state = state->BindExpr(U, V2); + + // Perform the store. + evalStore(Dst, NULL, U, *I2, state, loc, Result); + } + } +} + +void ExprEngine::VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); +} + +void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A, + AsmStmt::const_outputs_iterator I, + AsmStmt::const_outputs_iterator E, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + if (I == E) { + VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); + return; + } + + ExplodedNodeSet Tmp; + Visit(*I, Pred, Tmp); + ++I; + + for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) + VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); +} + +void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A, + AsmStmt::const_inputs_iterator I, + AsmStmt::const_inputs_iterator E, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + if (I == E) { + + // We have processed both the inputs and the outputs. All of the outputs + // should evaluate to Locs. Nuke all of their values. + + // FIXME: Some day in the future it would be nice to allow a "plug-in" + // which interprets the inline asm and stores proper results in the + // outputs. + + const GRState* state = GetState(Pred); + + for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), + OE = A->end_outputs(); OI != OE; ++OI) { + + SVal X = state->getSVal(*OI); + assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. + + if (isa<Loc>(X)) + state = state->bindLoc(cast<Loc>(X), UnknownVal()); + } + + MakeNode(Dst, A, Pred, state); + return; + } + + ExplodedNodeSet Tmp; + Visit(*I, Pred, Tmp); + + ++I; + + for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI) + VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); +} + +void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet Src; + if (const Expr *RetE = RS->getRetValue()) { + // Record the returned expression in the state. It will be used in + // processCallExit to bind the return value to the call expr. + { + static int tag = 0; + const GRState *state = GetState(Pred); + state = state->set<ReturnExpr>(RetE); + Pred = Builder->generateNode(RetE, state, Pred, &tag); + } + // We may get a NULL Pred because we generated a cached node. + if (Pred) + Visit(RetE, Pred, Src); + } + else { + Src.Add(Pred); + } + + ExplodedNodeSet CheckedSet; + CheckerVisit(RS, CheckedSet, Src, PreVisitStmtCallback); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I != E; ++I) { + + assert(Builder && "StmtNodeBuilder must be defined."); + + Pred = *I; + unsigned size = Dst.size(); + + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->hasGeneratedNode); + + getTF().evalReturn(Dst, *this, *Builder, RS, Pred); + + // Handle the case where no nodes where generated. + if (!Builder->BuildSinks && Dst.size() == size && + !Builder->hasGeneratedNode) + MakeNode(Dst, RS, Pred, GetState(Pred)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Binary operators. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + ExplodedNodeSet Tmp1; + Expr* LHS = B->getLHS()->IgnoreParens(); + Expr* RHS = B->getRHS()->IgnoreParens(); + + Visit(LHS, Pred, Tmp1); + ExplodedNodeSet Tmp3; + + for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { + SVal LeftV = GetState(*I1)->getSVal(LHS); + ExplodedNodeSet Tmp2; + Visit(RHS, *I1, Tmp2); + + ExplodedNodeSet CheckedSet; + CheckerVisit(B, CheckedSet, Tmp2, PreVisitStmtCallback); + + // With both the LHS and RHS evaluated, process the operation itself. + + for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end(); + I2 != E2; ++I2) { + + const GRState *state = GetState(*I2); + SVal RightV = state->getSVal(RHS); + + BinaryOperator::Opcode Op = B->getOpcode(); + + if (Op == BO_Assign) { + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV)) + { + unsigned Count = Builder->getCurrentBlockCount(); + RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count); + } + + SVal ExprVal = B->isLValue() ? LeftV : RightV; + + // Simulate the effects of a "store": bind the value of the RHS + // to the L-Value represented by the LHS. + evalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV); + continue; + } + + if (!B->isAssignmentOp()) { + // Process non-assignments except commas or short-circuited + // logical expressions (LAnd and LOr). + SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); + + if (Result.isUnknown()) { + MakeNode(Tmp3, B, *I2, state); + continue; + } + + state = state->BindExpr(B, Result); + + MakeNode(Tmp3, B, *I2, state); + continue; + } + + assert (B->isCompoundAssignmentOp()); + + switch (Op) { + default: + assert(0 && "Invalid opcode for compound assignment."); + case BO_MulAssign: Op = BO_Mul; break; + case BO_DivAssign: Op = BO_Div; break; + case BO_RemAssign: Op = BO_Rem; break; + case BO_AddAssign: Op = BO_Add; break; + case BO_SubAssign: Op = BO_Sub; break; + case BO_ShlAssign: Op = BO_Shl; break; + case BO_ShrAssign: Op = BO_Shr; break; + case BO_AndAssign: Op = BO_And; break; + case BO_XorAssign: Op = BO_Xor; break; + case BO_OrAssign: Op = BO_Or; break; + } + + // Perform a load (the LHS). This performs the checks for + // null dereferences, and so on. + ExplodedNodeSet Tmp4; + SVal location = state->getSVal(LHS); + evalLoad(Tmp4, LHS, *I2, state, location); + + for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4; + ++I4) { + state = GetState(*I4); + SVal V = state->getSVal(LHS); + + // Get the computation type. + QualType CTy = + cast<CompoundAssignOperator>(B)->getComputationResultType(); + CTy = getContext().getCanonicalType(CTy); + + QualType CLHSTy = + cast<CompoundAssignOperator>(B)->getComputationLHSType(); + CLHSTy = getContext().getCanonicalType(CLHSTy); + + QualType LTy = getContext().getCanonicalType(LHS->getType()); + + // Promote LHS. + V = svalBuilder.evalCast(V, CLHSTy, LTy); + + // Compute the result of the operation. + SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy), + B->getType(), CTy); + + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + + SVal LHSVal; + + if (Result.isUnknown() || + !getConstraintManager().canReasonAbout(Result)) { + + unsigned Count = Builder->getCurrentBlockCount(); + + // The symbolic value is actually for the type of the left-hand side + // expression, not the computation type, as this is the value the + // LValue on the LHS will bind to. + LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count); + + // However, we need to convert the symbol to the computation type. + Result = svalBuilder.evalCast(LHSVal, CTy, LTy); + } + else { + // The left-hand side may bind to a different value then the + // computation type. + LHSVal = svalBuilder.evalCast(Result, LTy, CTy); + } + + // In C++, assignment and compound assignment operators return an + // lvalue. + if (B->isLValue()) + state = state->BindExpr(B, location); + else + state = state->BindExpr(B, Result); + + evalStore(Tmp3, B, LHS, *I4, state, location, LHSVal); + } + } + } + + CheckerVisit(B, Dst, Tmp3, PostVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Checker registration/lookup. +//===----------------------------------------------------------------------===// + +Checker *ExprEngine::lookupChecker(void *tag) const { + CheckerMap::const_iterator I = CheckerM.find(tag); + return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; +} + +//===----------------------------------------------------------------------===// +// Visualization. +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG +static ExprEngine* GraphPrintCheckerState; +static SourceManager* GraphPrintSourceManager; + +namespace llvm { +template<> +struct DOTGraphTraits<ExplodedNode*> : + public DefaultDOTGraphTraits { + + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + + // FIXME: Since we do not cache error nodes in ExprEngine now, this does not + // work. + static std::string getNodeAttributes(const ExplodedNode* N, void*) { + +#if 0 + // FIXME: Replace with a general scheme to tell if the node is + // an error node. + if (GraphPrintCheckerState->isImplicitNullDeref(N) || + GraphPrintCheckerState->isExplicitNullDeref(N) || + GraphPrintCheckerState->isUndefDeref(N) || + GraphPrintCheckerState->isUndefStore(N) || + GraphPrintCheckerState->isUndefControlFlow(N) || + GraphPrintCheckerState->isUndefResult(N) || + GraphPrintCheckerState->isBadCall(N) || + GraphPrintCheckerState->isUndefArg(N)) + return "color=\"red\",style=\"filled\""; + + if (GraphPrintCheckerState->isNoReturnCall(N)) + return "color=\"blue\",style=\"filled\""; +#endif + return ""; + } + + static std::string getNodeLabel(const ExplodedNode* N, void*){ + + std::string sbuf; + llvm::raw_string_ostream Out(sbuf); + + // Program Location. + ProgramPoint Loc = N->getLocation(); + + switch (Loc.getKind()) { + case ProgramPoint::BlockEntranceKind: + Out << "Block Entrance: B" + << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); + break; + + case ProgramPoint::BlockExitKind: + assert (false); + break; + + case ProgramPoint::CallEnterKind: + Out << "CallEnter"; + break; + + case ProgramPoint::CallExitKind: + Out << "CallExit"; + break; + + default: { + if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { + const Stmt* S = L->getStmt(); + SourceLocation SLoc = S->getLocStart(); + + Out << S->getStmtClassName() << ' ' << (void*) S << ' '; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) + << "\\l"; + } + + if (isa<PreStmt>(Loc)) + Out << "\\lPreStmt\\l;"; + else if (isa<PostLoad>(Loc)) + Out << "\\lPostLoad\\l;"; + else if (isa<PostStore>(Loc)) + Out << "\\lPostStore\\l"; + else if (isa<PostLValue>(Loc)) + Out << "\\lPostLValue\\l"; + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isImplicitNullDeref(N)) + Out << "\\|Implicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isExplicitNullDeref(N)) + Out << "\\|Explicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isUndefDeref(N)) + Out << "\\|Dereference of undefialied value.\\l"; + else if (GraphPrintCheckerState->isUndefStore(N)) + Out << "\\|Store to Undefined Loc."; + else if (GraphPrintCheckerState->isUndefResult(N)) + Out << "\\|Result of operation is undefined."; + else if (GraphPrintCheckerState->isNoReturnCall(N)) + Out << "\\|Call to function marked \"noreturn\"."; + else if (GraphPrintCheckerState->isBadCall(N)) + Out << "\\|Call to NULL/Undefined."; + else if (GraphPrintCheckerState->isUndefArg(N)) + Out << "\\|Argument in call is undefined"; +#endif + + break; + } + + const BlockEdge& E = cast<BlockEdge>(Loc); + Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" + << E.getDst()->getBlockID() << ')'; + + if (const Stmt* T = E.getSrc()->getTerminator()) { + + SourceLocation SLoc = T->getLocStart(); + + Out << "\\|Terminator: "; + LangOptions LO; // FIXME. + E.getSrc()->printTerminator(Out, LO); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc); + } + + if (isa<SwitchStmt>(T)) { + const Stmt* Label = E.getDst()->getLabel(); + + if (Label) { + if (const CaseStmt* C = dyn_cast<CaseStmt>(Label)) { + Out << "\\lcase "; + LangOptions LO; // FIXME. + C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); + + if (const Stmt* RHS = C->getRHS()) { + Out << " .. "; + RHS->printPretty(Out, 0, PrintingPolicy(LO)); + } + + Out << ":"; + } + else { + assert (isa<DefaultStmt>(Label)); + Out << "\\ldefault:"; + } + } + else + Out << "\\l(implicit) default:"; + } + else if (isa<IndirectGotoStmt>(T)) { + // FIXME + } + else { + Out << "\\lCondition: "; + if (*E.getSrc()->succ_begin() == E.getDst()) + Out << "true"; + else + Out << "false"; + } + + Out << "\\l"; + } + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isUndefControlFlow(N)) { + Out << "\\|Control-flow based on\\lUndefined value.\\l"; + } +#endif + } + } + + const GRState *state = N->getState(); + Out << "\\|StateID: " << (void*) state + << " NodeID: " << (void*) N << "\\|"; + state->printDOT(Out, *N->getLocationContext()->getCFG()); + Out << "\\l"; + return Out.str(); + } +}; +} // end llvm namespace +#endif + +#ifndef NDEBUG +template <typename ITERATOR> +ExplodedNode* GetGraphNode(ITERATOR I) { return *I; } + +template <> ExplodedNode* +GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator> + (llvm::DenseMap<ExplodedNode*, Expr*>::iterator I) { + return I->first; +} +#endif + +void ExprEngine::ViewGraph(bool trim) { +#ifndef NDEBUG + if (trim) { + std::vector<ExplodedNode*> Src; + + // Flush any outstanding reports to make sure we cover all the nodes. + // This does not cause them to get displayed. + for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) + const_cast<BugType*>(*I)->FlushReports(BR); + + // Iterate through the reports and get their nodes. + for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) { + for (BugType::const_iterator I2=(*I)->begin(), E2=(*I)->end(); + I2!=E2; ++I2) { + const BugReportEquivClass& EQ = *I2; + const BugReport &R = **EQ.begin(); + ExplodedNode *N = const_cast<ExplodedNode*>(R.getErrorNode()); + if (N) Src.push_back(N); + } + } + + ViewGraph(&Src[0], &Src[0]+Src.size()); + } + else { + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + llvm::ViewGraph(*G.roots_begin(), "ExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; + } +#endif +} + +void ExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { +#ifndef NDEBUG + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + std::auto_ptr<ExplodedGraph> TrimmedG(G.Trim(Beg, End).first); + + if (!TrimmedG.get()) + llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; + else + llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; +#endif +} diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp new file mode 100644 index 0000000..fe628a2 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -0,0 +1,77 @@ +//=== 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. +// +//===----------------------------------------------------------------------===// +// +// This files defines FixedAddressChecker, a builtin checker that checks for +// assignment of a fixed address to a pointer. +// This check corresponds to CWE-587. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class FixedAddressChecker + : public CheckerVisitor<FixedAddressChecker> { + BuiltinBug *BT; +public: + FixedAddressChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *FixedAddressChecker::getTag() { + static int x; + return &x; +} + +void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // Using a fixed address is not portable because that address will probably + // not be valid in all environments or platforms. + + if (B->getOpcode() != BO_Assign) + return; + + QualType T = B->getType(); + if (!T->isPointerType()) + return; + + const GRState *state = C.getState(); + + SVal RV = state->getSVal(B->getRHS()); + + if (!RV.isConstant() || RV.isZeroConstant()) + return; + + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Use fixed address", + "Using a fixed address is not portable because that " + "address will probably not be valid in all " + "environments or platforms."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getRHS()->getSourceRange()); + C.EmitReport(R); + } +} + +static void RegisterFixedAddressChecker(ExprEngine &Eng) { + Eng.registerCheck(new FixedAddressChecker()); +} + +void ento::registerFixedAddressChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterFixedAddressChecker); +} diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp new file mode 100644 index 0000000..f49b125 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -0,0 +1,822 @@ +//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- 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 set of path-sensitive checks for idempotent and/or +// tautological operations. Each potential operation is checked along all paths +// to see if every path results in a pointless operation. +// +-------------------------------------------+ +// |Table of idempotent/tautological operations| +// +-------------------------------------------+ +//+--------------------------------------------------------------------------+ +//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op x | +//+--------------------------------------------------------------------------+ +// +, += | | | | x | x | | +// -, -= | | | | x | -x | | +// *, *= | | x | x | 0 | 0 | | +// /, /= | 1 | x | | N/A | 0 | | +// &, &= | x | | | 0 | 0 | x | x +// |, |= | x | | | x | x | ~0 | ~0 +// ^, ^= | 0 | | | x | x | | +// <<, <<= | | | | x | 0 | | +// >>, >>= | | | | x | 0 | | +// || | 1 | 1 | 1 | x | x | 1 | 1 +// && | 1 | x | x | 0 | 0 | x | x +// = | x | | | | | | +// == | 1 | | | | | | +// >= | 1 | | | | | | +// <= | 1 | | | | | | +// > | 0 | | | | | | +// < | 0 | | | | | | +// != | 0 | | | | | | +//===----------------------------------------------------------------------===// +// +// Things TODO: +// - Improved error messages +// - Handle mixed assumptions (which assumptions can belong together?) +// - Finer grained false positive control (levels) +// - Handling ~0 values + +#include "ClangSACheckers.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/AST/Stmt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/Support/ErrorHandling.h" +#include <deque> + +using namespace clang; +using namespace ento; + +namespace { +class IdempotentOperationChecker + : public CheckerVisitor<IdempotentOperationChecker> { +public: + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng); + +private: + // Our assumption about a particular operation. + enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0, + RHSis0 }; + + void UpdateAssumption(Assumption &A, const Assumption &New); + + // False positive reduction methods + static bool isSelfAssign(const Expr *LHS, const Expr *RHS); + static bool isUnused(const Expr *E, AnalysisContext *AC); + static bool isTruncationExtensionAssignment(const Expr *LHS, + const Expr *RHS); + bool pathWasCompletelyAnalyzed(const CFG *cfg, + const CFGBlock *CB, + const CFGStmtMap *CBM, + const CoreEngine &CE); + static bool CanVary(const Expr *Ex, + AnalysisContext *AC); + static bool isConstantOrPseudoConstant(const DeclRefExpr *DR, + AnalysisContext *AC); + static bool containsNonLocalVarDecl(const Stmt *S); + + // Hash table and related data structures + struct BinaryOperatorData { + BinaryOperatorData() : assumption(Possible), analysisContext(0) {} + + Assumption assumption; + AnalysisContext *analysisContext; + ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a + // BinaryOperator + }; + typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData> + AssumptionMap; + AssumptionMap hash; + + // A class that performs reachability queries for CFGBlocks. Several internal + // checks in this checker require reachability information. The requests all + // tend to have a common destination, so we lazily do a predecessor search + // from the destination node and cache the results to prevent work + // duplication. + class CFGReachabilityAnalysis { + typedef llvm::BitVector ReachableSet; + typedef llvm::DenseMap<unsigned, ReachableSet> ReachableMap; + ReachableSet analyzed; + ReachableMap reachable; + public: + CFGReachabilityAnalysis(const CFG &cfg) + : analyzed(cfg.getNumBlockIDs(), false) {} + + inline bool isReachable(const CFGBlock *Src, const CFGBlock *Dst); + private: + void MapReachability(const CFGBlock *Dst); + }; + llvm::OwningPtr<CFGReachabilityAnalysis> CRA; +}; +} + +void *IdempotentOperationChecker::getTag() { + static int x = 0; + return &x; +} + +static void RegisterIdempotentOperationChecker(ExprEngine &Eng) { + Eng.registerCheck(new IdempotentOperationChecker()); +} + +void ento::registerIdempotentOperationChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterIdempotentOperationChecker); +} + +void IdempotentOperationChecker::PreVisitBinaryOperator( + CheckerContext &C, + const BinaryOperator *B) { + // Find or create an entry in the hash for this BinaryOperator instance. + // If we haven't done a lookup before, it will get default initialized to + // 'Possible'. At this stage we do not store the ExplodedNode, as it has not + // been created yet. + BinaryOperatorData &Data = hash[B]; + Assumption &A = Data.assumption; + AnalysisContext *AC = C.getCurrentAnalysisContext(); + Data.analysisContext = AC; + + // If we already have visited this node on a path that does not contain an + // idempotent operation, return immediately. + if (A == Impossible) + return; + + // Retrieve both sides of the operator and determine if they can vary (which + // may mean this is a false positive. + const Expr *LHS = B->getLHS(); + const Expr *RHS = B->getRHS(); + + // At this stage we can calculate whether each side contains a false positive + // that applies to all operators. We only need to calculate this the first + // time. + bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false; + if (A == Possible) { + // An expression contains a false positive if it can't vary, or if it + // contains a known false positive VarDecl. + LHSContainsFalsePositive = !CanVary(LHS, AC) + || containsNonLocalVarDecl(LHS); + RHSContainsFalsePositive = !CanVary(RHS, AC) + || containsNonLocalVarDecl(RHS); + } + + const GRState *state = C.getState(); + + SVal LHSVal = state->getSVal(LHS); + SVal RHSVal = state->getSVal(RHS); + + // If either value is unknown, we can't be 100% sure of all paths. + if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) { + A = Impossible; + return; + } + BinaryOperator::Opcode Op = B->getOpcode(); + + // Dereference the LHS SVal if this is an assign operation + switch (Op) { + default: + break; + + // Fall through intentional + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_Assign: + // Assign statements have one extra level of indirection + if (!isa<Loc>(LHSVal)) { + A = Impossible; + return; + } + LHSVal = state->getSVal(cast<Loc>(LHSVal), LHS->getType()); + } + + + // We now check for various cases which result in an idempotent operation. + + // x op x + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_Assign: + // x Assign x can be used to silence unused variable warnings intentionally. + // If this is a self assignment and the variable is referenced elsewhere, + // and the assignment is not a truncation or extension, then it is a false + // positive. + if (isSelfAssign(LHS, RHS)) { + if (!isUnused(LHS, AC) && !isTruncationExtensionAssignment(LHS, RHS)) { + UpdateAssumption(A, Equal); + return; + } + else { + A = Impossible; + return; + } + } + + case BO_SubAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_Sub: + case BO_Div: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_LOr: + case BO_LAnd: + case BO_EQ: + case BO_NE: + if (LHSVal != RHSVal || LHSContainsFalsePositive + || RHSContainsFalsePositive) + break; + UpdateAssumption(A, Equal); + return; + } + + // x op 1 + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_MulAssign: + case BO_DivAssign: + case BO_Mul: + case BO_Div: + case BO_LOr: + case BO_LAnd: + if (!RHSVal.isConstant(1) || RHSContainsFalsePositive) + break; + UpdateAssumption(A, RHSis1); + return; + } + + // 1 op x + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_MulAssign: + case BO_Mul: + case BO_LOr: + case BO_LAnd: + if (!LHSVal.isConstant(1) || LHSContainsFalsePositive) + break; + UpdateAssumption(A, LHSis1); + return; + } + + // x op 0 + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_Add: + case BO_Sub: + case BO_Mul: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_Shl: + case BO_Shr: + case BO_LOr: + case BO_LAnd: + if (!RHSVal.isConstant(0) || RHSContainsFalsePositive) + break; + UpdateAssumption(A, RHSis0); + return; + } + + // 0 op x + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + //case BO_AddAssign: // Common false positive + case BO_SubAssign: // Check only if unsigned + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + //case BO_OrAssign: // Common false positive + //case BO_XorAssign: // Common false positive + case BO_ShlAssign: + case BO_ShrAssign: + case BO_Add: + case BO_Sub: + case BO_Mul: + case BO_Div: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_Shl: + case BO_Shr: + case BO_LOr: + case BO_LAnd: + if (!LHSVal.isConstant(0) || LHSContainsFalsePositive) + break; + UpdateAssumption(A, LHSis0); + return; + } + + // If we get to this point, there has been a valid use of this operation. + A = Impossible; +} + +// At the post visit stage, the predecessor ExplodedNode will be the +// BinaryOperator that was just created. We use this hook to collect the +// ExplodedNode. +void IdempotentOperationChecker::PostVisitBinaryOperator( + CheckerContext &C, + const BinaryOperator *B) { + // Add the ExplodedNode we just visited + BinaryOperatorData &Data = hash[B]; + + const Stmt *predStmt + = cast<StmtPoint>(C.getPredecessor()->getLocation()).getStmt(); + + // Ignore implicit calls to setters. + if (isa<ObjCPropertyRefExpr>(predStmt)) + return; + + assert(isa<BinaryOperator>(predStmt)); + Data.explodedNodes.Add(C.getPredecessor()); +} + +void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) { + BugType *BT = new BugType("Idempotent operation", "Dead code"); + // Iterate over the hash to see if we have any paths with definite + // idempotent operations. + for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { + // Unpack the hash contents + const BinaryOperatorData &Data = i->second; + const Assumption &A = Data.assumption; + AnalysisContext *AC = Data.analysisContext; + const ExplodedNodeSet &ES = Data.explodedNodes; + + const BinaryOperator *B = i->first; + + if (A == Impossible) + continue; + + // If the analyzer did not finish, check to see if we can still emit this + // warning + if (Eng.hasWorkRemaining()) { + const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(), + &AC->getParentMap()); + + // If we can trace back + if (!pathWasCompletelyAnalyzed(AC->getCFG(), + CBM->getBlock(B), CBM, + Eng.getCoreEngine())) + continue; + + delete CBM; + } + + // Select the error message and SourceRanges to report. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + bool LHSRelevant = false, RHSRelevant = false; + switch (A) { + case Equal: + LHSRelevant = true; + RHSRelevant = true; + if (B->getOpcode() == BO_Assign) + os << "Assigned value is always the same as the existing value"; + else + os << "Both operands to '" << B->getOpcodeStr() + << "' always have the same value"; + break; + case LHSis1: + LHSRelevant = true; + os << "The left operand to '" << B->getOpcodeStr() << "' is always 1"; + break; + case RHSis1: + RHSRelevant = true; + os << "The right operand to '" << B->getOpcodeStr() << "' is always 1"; + break; + case LHSis0: + LHSRelevant = true; + os << "The left operand to '" << B->getOpcodeStr() << "' is always 0"; + break; + case RHSis0: + RHSRelevant = true; + os << "The right operand to '" << B->getOpcodeStr() << "' is always 0"; + break; + case Possible: + llvm_unreachable("Operation was never marked with an assumption"); + case Impossible: + llvm_unreachable(0); + } + + // Add a report for each ExplodedNode + for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) { + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I); + + // Add source ranges and visitor hooks + if (LHSRelevant) { + const Expr *LHS = i->first->getLHS(); + report->addRange(LHS->getSourceRange()); + report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS); + } + if (RHSRelevant) { + const Expr *RHS = i->first->getRHS(); + report->addRange(i->first->getRHS()->getSourceRange()); + report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS); + } + + BR.EmitReport(report); + } + } +} + +// Updates the current assumption given the new assumption +inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A, + const Assumption &New) { +// If the assumption is the same, there is nothing to do + if (A == New) + return; + + switch (A) { + // If we don't currently have an assumption, set it + case Possible: + A = New; + return; + + // If we have determined that a valid state happened, ignore the new + // assumption. + case Impossible: + return; + + // Any other case means that we had a different assumption last time. We don't + // currently support mixing assumptions for diagnostic reasons, so we set + // our assumption to be impossible. + default: + A = Impossible; + return; + } +} + +// Check for a statement where a variable is self assigned to possibly avoid an +// unused variable warning. +bool IdempotentOperationChecker::isSelfAssign(const Expr *LHS, const Expr *RHS) { + LHS = LHS->IgnoreParenCasts(); + RHS = RHS->IgnoreParenCasts(); + + const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS); + if (!LHS_DR) + return false; + + const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl()); + if (!VD) + return false; + + const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS); + if (!RHS_DR) + return false; + + if (VD != RHS_DR->getDecl()) + return false; + + return true; +} + +// Returns true if the Expr points to a VarDecl that is not read anywhere +// outside of self-assignments. +bool IdempotentOperationChecker::isUnused(const Expr *E, + AnalysisContext *AC) { + if (!E) + return false; + + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (!DR) + return false; + + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return false; + + if (AC->getPseudoConstantAnalysis()->wasReferenced(VD)) + return false; + + return true; +} + +// Check for self casts truncating/extending a variable +bool IdempotentOperationChecker::isTruncationExtensionAssignment( + const Expr *LHS, + const Expr *RHS) { + + const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParenCasts()); + if (!LHS_DR) + return false; + + const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl()); + if (!VD) + return false; + + const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS->IgnoreParenCasts()); + if (!RHS_DR) + return false; + + if (VD != RHS_DR->getDecl()) + return false; + + return dyn_cast<DeclRefExpr>(RHS->IgnoreParenLValueCasts()) == NULL; +} + +// Returns false if a path to this block was not completely analyzed, or true +// otherwise. +bool +IdempotentOperationChecker::pathWasCompletelyAnalyzed(const CFG *cfg, + const CFGBlock *CB, + const CFGStmtMap *CBM, + const CoreEngine &CE) { + + if (!CRA.get()) + CRA.reset(new CFGReachabilityAnalysis(*cfg)); + + // Test for reachability from any aborted blocks to this block + typedef CoreEngine::BlocksAborted::const_iterator AbortedIterator; + for (AbortedIterator I = CE.blocks_aborted_begin(), + E = CE.blocks_aborted_end(); I != E; ++I) { + const BlockEdge &BE = I->first; + + // The destination block on the BlockEdge is the first block that was not + // analyzed. If we can reach this block from the aborted block, then this + // block was not completely analyzed. + // + // Also explicitly check if the current block is the destination block. + // While technically reachable, it means we aborted the analysis on + // a path that included that block. + const CFGBlock *destBlock = BE.getDst(); + if (destBlock == CB || CRA->isReachable(destBlock, CB)) + return false; + } + + // For the items still on the worklist, see if they are in blocks that + // can eventually reach 'CB'. + class VisitWL : public WorkList::Visitor { + const CFGStmtMap *CBM; + const CFGBlock *TargetBlock; + CFGReachabilityAnalysis &CRA; + public: + VisitWL(const CFGStmtMap *cbm, const CFGBlock *targetBlock, + CFGReachabilityAnalysis &cra) + : CBM(cbm), TargetBlock(targetBlock), CRA(cra) {} + virtual bool visit(const WorkListUnit &U) { + ProgramPoint P = U.getNode()->getLocation(); + const CFGBlock *B = 0; + if (StmtPoint *SP = dyn_cast<StmtPoint>(&P)) { + B = CBM->getBlock(SP->getStmt()); + } + else if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + B = BE->getDst(); + } + else if (BlockEntrance *BEnt = dyn_cast<BlockEntrance>(&P)) { + B = BEnt->getBlock(); + } + else if (BlockExit *BExit = dyn_cast<BlockExit>(&P)) { + B = BExit->getBlock(); + } + if (!B) + return true; + + return CRA.isReachable(B, TargetBlock); + } + }; + VisitWL visitWL(CBM, CB, *CRA.get()); + // Were there any items in the worklist that could potentially reach + // this block? + if (CE.getWorkList()->visitItemsInWorkList(visitWL)) + return false; + + // Verify that this block is reachable from the entry block + if (!CRA->isReachable(&cfg->getEntry(), CB)) + return false; + + // If we get to this point, there is no connection to the entry block or an + // aborted block. This path is unreachable and we can report the error. + return true; +} + +// Recursive function that determines whether an expression contains any element +// that varies. This could be due to a compile-time constant like sizeof. An +// expression may also involve a variable that behaves like a constant. The +// function returns true if the expression varies, and false otherwise. +bool IdempotentOperationChecker::CanVary(const Expr *Ex, + AnalysisContext *AC) { + // Parentheses and casts are irrelevant here + Ex = Ex->IgnoreParenCasts(); + + if (Ex->getLocStart().isMacroID()) + return false; + + switch (Ex->getStmtClass()) { + // Trivially true cases + case Stmt::ArraySubscriptExprClass: + case Stmt::MemberExprClass: + case Stmt::StmtExprClass: + case Stmt::CallExprClass: + case Stmt::VAArgExprClass: + case Stmt::ShuffleVectorExprClass: + return true; + default: + return true; + + // Trivially false cases + case Stmt::IntegerLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::PredefinedExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::StringLiteralClass: + case Stmt::OffsetOfExprClass: + case Stmt::CompoundLiteralExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::BinaryTypeTraitExprClass: + case Stmt::GNUNullExprClass: + case Stmt::InitListExprClass: + case Stmt::DesignatedInitExprClass: + case Stmt::BlockExprClass: + case Stmt::BlockDeclRefExprClass: + return false; + + // Cases requiring custom logic + case Stmt::SizeOfAlignOfExprClass: { + const SizeOfAlignOfExpr *SE = cast<const SizeOfAlignOfExpr>(Ex); + if (!SE->isSizeOf()) + return false; + return SE->getTypeOfArgument()->isVariableArrayType(); + } + case Stmt::DeclRefExprClass: + // Check for constants/pseudoconstants + return !isConstantOrPseudoConstant(cast<DeclRefExpr>(Ex), AC); + + // The next cases require recursion for subexpressions + case Stmt::BinaryOperatorClass: { + const BinaryOperator *B = cast<const BinaryOperator>(Ex); + + // Exclude cases involving pointer arithmetic. These are usually + // false positives. + if (B->getOpcode() == BO_Sub || B->getOpcode() == BO_Add) + if (B->getLHS()->getType()->getAs<PointerType>()) + return false; + + return CanVary(B->getRHS(), AC) + || CanVary(B->getLHS(), AC); + } + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast<const UnaryOperator>(Ex); + // Handle trivial case first + switch (U->getOpcode()) { + case UO_Extension: + return false; + default: + return CanVary(U->getSubExpr(), AC); + } + } + case Stmt::ChooseExprClass: + return CanVary(cast<const ChooseExpr>(Ex)->getChosenSubExpr( + AC->getASTContext()), AC); + case Stmt::ConditionalOperatorClass: + case Stmt::BinaryConditionalOperatorClass: + return CanVary(cast<AbstractConditionalOperator>(Ex)->getCond(), AC); + } +} + +// Returns true if a DeclRefExpr is or behaves like a constant. +bool IdempotentOperationChecker::isConstantOrPseudoConstant( + const DeclRefExpr *DR, + AnalysisContext *AC) { + // Check if the type of the Decl is const-qualified + if (DR->getType().isConstQualified()) + return true; + + // Check for an enum + if (isa<EnumConstantDecl>(DR->getDecl())) + return true; + + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return true; + + // Check if the Decl behaves like a constant. This check also takes care of + // static variables, which can only change between function calls if they are + // modified in the AST. + PseudoConstantAnalysis *PCA = AC->getPseudoConstantAnalysis(); + if (PCA->isPseudoConstant(VD)) + return true; + + return false; +} + +// Recursively find any substatements containing VarDecl's with storage other +// than local +bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); + + if (DR) + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + if (!VD->hasLocalStorage()) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsNonLocalVarDecl(child)) + return true; + + return false; +} + +bool IdempotentOperationChecker::CFGReachabilityAnalysis::isReachable( + const CFGBlock *Src, + const CFGBlock *Dst) { + const unsigned DstBlockID = Dst->getBlockID(); + + // If we haven't analyzed the destination node, run the analysis now + if (!analyzed[DstBlockID]) { + MapReachability(Dst); + analyzed[DstBlockID] = true; + } + + // Return the cached result + return reachable[DstBlockID][Src->getBlockID()]; +} + +// Maps reachability to a common node by walking the predecessors of the +// destination node. +void IdempotentOperationChecker::CFGReachabilityAnalysis::MapReachability( + const CFGBlock *Dst) { + + llvm::SmallVector<const CFGBlock *, 11> worklist; + llvm::BitVector visited(analyzed.size()); + + ReachableSet &DstReachability = reachable[Dst->getBlockID()]; + DstReachability.resize(analyzed.size(), false); + + // Start searching from the destination node, since we commonly will perform + // multiple queries relating to a destination node. + worklist.push_back(Dst); + bool firstRun = true; + + while (!worklist.empty()) { + const CFGBlock *block = worklist.back(); + worklist.pop_back(); + + if (visited[block->getBlockID()]) + continue; + visited[block->getBlockID()] = true; + + // Update reachability information for this node -> Dst + if (!firstRun) { + // Don't insert Dst -> Dst unless it was a predecessor of itself + DstReachability[block->getBlockID()] = true; + } + else + firstRun = false; + + // Add the predecessors to the worklist. + for (CFGBlock::const_pred_iterator i = block->pred_begin(), + e = block->pred_end(); i != e; ++i) { + worklist.push_back(*i); + } + } +} diff --git a/lib/StaticAnalyzer/Checkers/InternalChecks.h b/lib/StaticAnalyzer/Checkers/InternalChecks.h new file mode 100644 index 0000000..e855386 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/InternalChecks.h @@ -0,0 +1,51 @@ +//=-- InternalChecks.h- Builtin ExprEngine Checks -------------------*- 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 functions to instantiate and register the "built-in" +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ExprEngine_INTERNAL_CHECKS +#define LLVM_CLANG_GR_ExprEngine_INTERNAL_CHECKS + +namespace clang { + +namespace ento { + +class ExprEngine; + +// Foundational checks that handle basic semantics. +void RegisterAdjustedReturnValueChecker(ExprEngine &Eng); +void RegisterArrayBoundChecker(ExprEngine &Eng); +void RegisterArrayBoundCheckerV2(ExprEngine &Eng); +void RegisterAttrNonNullChecker(ExprEngine &Eng); +void RegisterBuiltinFunctionChecker(ExprEngine &Eng); +void RegisterCallAndMessageChecker(ExprEngine &Eng); +void RegisterCastSizeChecker(ExprEngine &Eng); +void RegisterDereferenceChecker(ExprEngine &Eng); +void RegisterDivZeroChecker(ExprEngine &Eng); +void RegisterNoReturnFunctionChecker(ExprEngine &Eng); +void RegisterReturnPointerRangeChecker(ExprEngine &Eng); +void RegisterReturnUndefChecker(ExprEngine &Eng); +void RegisterUndefBranchChecker(ExprEngine &Eng); +void RegisterUndefCapturedBlockVarChecker(ExprEngine &Eng); +void RegisterUndefResultChecker(ExprEngine &Eng); +void RegisterUndefinedArraySubscriptChecker(ExprEngine &Eng); +void RegisterUndefinedAssignmentChecker(ExprEngine &Eng); +void RegisterVLASizeChecker(ExprEngine &Eng); + +// API checks. +void RegisterOSAtomicChecker(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp new file mode 100644 index 0000000..9e3adc8 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -0,0 +1,311 @@ +//=== 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. +// +//===----------------------------------------------------------------------===// +// +// This defines LLVMConventionsChecker, a bunch of small little checks +// for checking specific coding conventions in the LLVM/Clang codebase. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include <string> +#include "llvm/ADT/StringRef.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Generic type checking routines. +//===----------------------------------------------------------------------===// + +static bool IsLLVMStringRef(QualType T) { + const RecordType *RT = T->getAs<RecordType>(); + if (!RT) + return false; + + return llvm::StringRef(QualType(RT, 0).getAsString()) == + "class llvm::StringRef"; +} + +/// Check whether the declaration is semantically inside the top-level +/// namespace named by ns. +static bool InNamespace(const Decl *D, llvm::StringRef NS) { + const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); + if (!ND) + return false; + const IdentifierInfo *II = ND->getIdentifier(); + if (!II || !II->getName().equals(NS)) + return false; + return isa<TranslationUnitDecl>(ND->getDeclContext()); +} + +static bool IsStdString(QualType T) { + if (const ElaboratedType *QT = T->getAs<ElaboratedType>()) + T = QT->getNamedType(); + + const TypedefType *TT = T->getAs<TypedefType>(); + if (!TT) + return false; + + const TypedefDecl *TD = TT->getDecl(); + + if (!InNamespace(TD, "std")) + return false; + + return TD->getName() == "string"; +} + +static bool IsClangType(const RecordDecl *RD) { + return RD->getName() == "Type" && InNamespace(RD, "clang"); +} + +static bool IsClangDecl(const RecordDecl *RD) { + return RD->getName() == "Decl" && InNamespace(RD, "clang"); +} + +static bool IsClangStmt(const RecordDecl *RD) { + return RD->getName() == "Stmt" && InNamespace(RD, "clang"); +} + +static bool IsClangAttr(const RecordDecl *RD) { + return RD->getName() == "Attr" && InNamespace(RD, "clang"); +} + +static bool IsStdVector(QualType T) { + const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); + if (!TS) + return false; + + TemplateName TM = TS->getTemplateName(); + TemplateDecl *TD = TM.getAsTemplateDecl(); + + if (!TD || !InNamespace(TD, "std")) + return false; + + return TD->getName() == "vector"; +} + +static bool IsSmallVector(QualType T) { + const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); + if (!TS) + return false; + + TemplateName TM = TS->getTemplateName(); + TemplateDecl *TD = TM.getAsTemplateDecl(); + + if (!TD || !InNamespace(TD, "llvm")) + return false; + + return TD->getName() == "SmallVector"; +} + +//===----------------------------------------------------------------------===// +// CHECK: a llvm::StringRef should not be bound to a temporary std::string whose +// lifetime is shorter than the StringRef's. +//===----------------------------------------------------------------------===// + +namespace { +class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { + BugReporter &BR; +public: + StringRefCheckerVisitor(BugReporter &br) : BR(br) {} + void VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; + I != E; ++I) + if (Stmt *child = *I) + Visit(child); + } + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitDeclStmt(DeclStmt *DS); +private: + void VisitVarDecl(VarDecl *VD); +}; +} // end anonymous namespace + +static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { + StringRefCheckerVisitor walker(BR); + walker.Visit(D->getBody()); +} + +void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { + VisitChildren(S); + + for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) + if (VarDecl *VD = dyn_cast<VarDecl>(*I)) + VisitVarDecl(VD); +} + +void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { + Expr *Init = VD->getInit(); + if (!Init) + return; + + // Pattern match for: + // llvm::StringRef x = call() (where call returns std::string) + if (!IsLLVMStringRef(VD->getType())) + return; + ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); + if (!Ex1) + return; + CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); + if (!Ex2 || Ex2->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); + if (!Ex3) + return; + CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); + if (!Ex4 || Ex4->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); + if (!Ex5) + return; + CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); + if (!Ex6 || !IsStdString(Ex6->getType())) + return; + + // Okay, badness! Report an error. + const char *desc = "StringRef should not be bound to temporary " + "std::string that it outlives"; + + BR.EmitBasicReport(desc, "LLVM Conventions", desc, + VD->getLocStart(), Init->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// CHECK: Clang AST nodes should not have fields that can allocate +// memory. +//===----------------------------------------------------------------------===// + +static bool AllocatesMemory(QualType T) { + return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); +} + +// This type checking could be sped up via dynamic programming. +static bool IsPartOfAST(const CXXRecordDecl *R) { + if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R)) + return true; + + for (CXXRecordDecl::base_class_const_iterator I = R->bases_begin(), + E = R->bases_end(); I!=E; ++I) { + CXXBaseSpecifier BS = *I; + QualType T = BS.getType(); + if (const RecordType *baseT = T->getAs<RecordType>()) { + CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl()); + if (IsPartOfAST(baseD)) + return true; + } + } + + return false; +} + +namespace { +class ASTFieldVisitor { + llvm::SmallVector<FieldDecl*, 10> FieldChain; + const CXXRecordDecl *Root; + BugReporter &BR; +public: + ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br) + : Root(root), BR(br) {} + + void Visit(FieldDecl *D); + void ReportError(QualType T); +}; +} // end anonymous namespace + +static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR) { + if (!IsPartOfAST(R)) + return; + + for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end(); + I != E; ++I) { + ASTFieldVisitor walker(R, BR); + walker.Visit(*I); + } +} + +void ASTFieldVisitor::Visit(FieldDecl *D) { + FieldChain.push_back(D); + + QualType T = D->getType(); + + if (AllocatesMemory(T)) + ReportError(T); + + if (const RecordType *RT = T->getAs<RecordType>()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) + Visit(*I); + } + + FieldChain.pop_back(); +} + +void ASTFieldVisitor::ReportError(QualType T) { + llvm::SmallString<1024> buf; + llvm::raw_svector_ostream os(buf); + + os << "AST class '" << Root->getName() << "' has a field '" + << FieldChain.front()->getName() << "' that allocates heap memory"; + if (FieldChain.size() > 1) { + os << " via the following chain: "; + bool isFirst = true; + for (llvm::SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), + E=FieldChain.end(); I!=E; ++I) { + if (!isFirst) + os << '.'; + else + isFirst = false; + os << (*I)->getName(); + } + } + os << " (type " << FieldChain.back()->getType().getAsString() << ")"; + os.flush(); + + // Note that this will fire for every translation unit that uses this + // class. This is suboptimal, but at least scan-build will merge + // duplicate HTML reports. In the future we need a unified way of merging + // duplicate reports across translation units. For C++ classes we cannot + // just report warnings when we see an out-of-line method definition for a + // class, as that heuristic doesn't always work (the complete definition of + // the class may be in the header file, for example). + BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions", + os.str(), FieldChain.front()->getLocStart()); +} + +//===----------------------------------------------------------------------===// +// LLVMConventionsChecker +//===----------------------------------------------------------------------===// + +namespace { +class LLVMConventionsChecker : public CheckerV2< + check::ASTDecl<CXXRecordDecl>, + check::ASTCodeBody > { +public: + void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, + BugReporter &BR) const { + if (R->isDefinition()) + CheckASTMemory(R, BR); + } + + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + CheckStringRefAssignedTemporary(D, BR); + } +}; +} + +void ento::registerLLVMConventionsChecker(CheckerManager &mgr) { + mgr.registerChecker<LLVMConventionsChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp new file mode 100644 index 0000000..358be12 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -0,0 +1,146 @@ +// 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. +// +//===----------------------------------------------------------------------===// +// +// This defines MacOSXAPIChecker, which is an assortment of checks on calls +// to various, widely used Mac OS X functions. +// +// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated +// to here, using the new Checker interface. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class MacOSXAPIChecker : public CheckerVisitor<MacOSXAPIChecker> { + enum SubChecks { + DispatchOnce = 0, + DispatchOnceF, + NumChecks + }; + + BugType *BTypes[NumChecks]; + +public: + MacOSXAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } + static void *getTag() { static unsigned tag = 0; return &tag; } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} //end anonymous namespace + +static void RegisterMacOSXAPIChecker(ExprEngine &Eng) { + Eng.registerCheck(new MacOSXAPIChecker()); +} + +void ento::registerMacOSXAPIChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterMacOSXAPIChecker); +} + +//===----------------------------------------------------------------------===// +// dispatch_once and dispatch_once_f +//===----------------------------------------------------------------------===// + +static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, + BugType *&BT, const IdentifierInfo *FI) { + + if (!BT) { + llvm::SmallString<128> S; + llvm::raw_svector_ostream os(S); + os << "Improper use of '" << FI->getName() << '\''; + BT = new BugType(os.str(), "Mac OS X API"); + } + + if (CE->getNumArgs() < 1) + return; + + // Check if the first argument is stack allocated. If so, issue a warning + // because that's likely to be bad news. + const GRState *state = C.getState(); + const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); + if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) + return; + + ExplodedNode *N = C.generateSink(state); + if (!N) + return; + + llvm::SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to '" << FI->getName() << "' uses"; + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) + os << " the local variable '" << VR->getDecl()->getName() << '\''; + else + os << " stack allocated memory"; + os << " for the predicate value. Using such transient memory for " + "the predicate is potentially dangerous."; + if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) + os << " Perhaps you intended to declare the variable as 'static'?"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +typedef void (*SubChecker)(CheckerContext &C, const CallExpr *CE, BugType *&BT, + const IdentifierInfo *FI); +namespace { + class SubCheck { + SubChecker SC; + BugType **BT; + public: + SubCheck(SubChecker sc, BugType *& bt) : SC(sc), BT(&bt) {} + SubCheck() : SC(NULL), BT(NULL) {} + + void run(CheckerContext &C, const CallExpr *CE, + const IdentifierInfo *FI) const { + if (SC) + SC(C, CE, *BT, FI); + } + }; +} // end anonymous namespace + +void MacOSXAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + // FIXME: Mostly copy and paste from UnixAPIChecker. Should refactor. + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *Fn = + dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + + if (!Fn) + return; + + const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); + if (!FI) + return; + + const SubCheck &SC = + llvm::StringSwitch<SubCheck>(FI->getName()) + .Case("dispatch_once", SubCheck(CheckDispatchOnce, BTypes[DispatchOnce])) + .Case("dispatch_once_f", SubCheck(CheckDispatchOnce, + BTypes[DispatchOnceF])) + .Default(SubCheck()); + + SC.run(C, CE, FI); +} diff --git a/lib/StaticAnalyzer/Checkers/Makefile b/lib/StaticAnalyzer/Checkers/Makefile new file mode 100644 index 0000000..97f4642 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/Makefile @@ -0,0 +1,24 @@ +##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangStaticAnalyzerCheckers + +BUILT_SOURCES = Checkers.inc +TABLEGEN_INC_FILES_COMMON = 1 + +include $(CLANG_LEVEL)/Makefile + +$(ObjDir)/Checkers.inc.tmp : Checkers.td $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include/clang/StaticAnalyzer/Checkers/CheckerBase.td $(TBLGEN) $(ObjDir)/.dir + $(Echo) "Building Clang SA Checkers tables with tblgen" + $(Verb) $(TableGen) -gen-clang-sa-checkers -I $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -o $(call SYSPATH, $@) $< diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp new file mode 100644 index 0000000..9d3a887 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -0,0 +1,731 @@ +//=== 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines malloc/free checker, which checks for potential memory +// leaks, double free, and use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "ExperimentalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +using namespace clang; +using namespace ento; + +namespace { + +class RefState { + enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped, + Relinquished } K; + const Stmt *S; + +public: + RefState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isAllocated() const { return K == AllocateUnchecked; } + //bool isFailed() const { return K == AllocateFailed; } + bool isReleased() const { return K == Released; } + //bool isEscaped() const { return K == Escaped; } + //bool isRelinquished() const { return K == Relinquished; } + + bool operator==(const RefState &X) const { + return K == X.K && S == X.S; + } + + static RefState getAllocateUnchecked(const Stmt *s) { + return RefState(AllocateUnchecked, s); + } + static RefState getAllocateFailed() { + return RefState(AllocateFailed, 0); + } + static RefState getReleased(const Stmt *s) { return RefState(Released, s); } + static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } + static RefState getRelinquished(const Stmt *s) { + return RefState(Relinquished, s); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + +class RegionState {}; + +class MallocChecker : public CheckerVisitor<MallocChecker> { + BuiltinBug *BT_DoubleFree; + BuiltinBug *BT_Leak; + BuiltinBug *BT_UseFree; + BuiltinBug *BT_UseRelinquished; + BuiltinBug *BT_BadFree; + IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc; + +public: + MallocChecker() + : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_UseRelinquished(0), + BT_BadFree(0), + II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {} + static void *getTag(); + bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); + void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + const GRState *evalAssume(const GRState *state, SVal Cond, bool Assumption, + bool *respondsToCallback); + void visitLocation(CheckerContext &C, const Stmt *S, SVal l, bool isLoad); + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val); + +private: + void MallocMem(CheckerContext &C, const CallExpr *CE); + void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att); + const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + const Expr *SizeEx, SVal Init, + const GRState *state) { + return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state); + } + const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + SVal SizeEx, SVal Init, + const GRState *state); + + void FreeMem(CheckerContext &C, const CallExpr *CE); + void FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att); + const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state, unsigned Num, bool Hold); + + void ReallocMem(CheckerContext &C, const CallExpr *CE); + void CallocMem(CheckerContext &C, const CallExpr *CE); + + bool SummarizeValue(llvm::raw_ostream& os, SVal V); + bool SummarizeRegion(llvm::raw_ostream& os, const MemRegion *MR); + void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range); +}; +} // end anonymous namespace + +typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy; + +namespace clang { +namespace ento { + template <> + struct GRStateTrait<RegionState> + : public GRStatePartialTrait<RegionStateTy> { + static void *GDMIndex() { return MallocChecker::getTag(); } + }; +} +} + +void ento::RegisterMallocChecker(ExprEngine &Eng) { + Eng.registerCheck(new MallocChecker()); +} + +void *MallocChecker::getTag() { + static int x; + return &x; +} + +bool MallocChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + ASTContext &Ctx = C.getASTContext(); + if (!II_malloc) + II_malloc = &Ctx.Idents.get("malloc"); + if (!II_free) + II_free = &Ctx.Idents.get("free"); + if (!II_realloc) + II_realloc = &Ctx.Idents.get("realloc"); + if (!II_calloc) + II_calloc = &Ctx.Idents.get("calloc"); + + if (FD->getIdentifier() == II_malloc) { + MallocMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_free) { + FreeMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_realloc) { + ReallocMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_calloc) { + CallocMem(C, CE); + return true; + } + + // Check all the attributes, if there are any. + // There can be multiple of these attributes. + bool rv = false; + if (FD->hasAttrs()) { + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) { + switch ((*i)->getOwnKind()) { + case OwnershipAttr::Returns: { + MallocMemReturnsAttr(C, CE, *i); + rv = true; + break; + } + case OwnershipAttr::Takes: + case OwnershipAttr::Holds: { + FreeMemAttr(C, CE, *i); + rv = true; + break; + } + default: + break; + } + } + } + return rv; +} + +void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + C.getState()); + C.addTransition(state); +} + +void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) { + if (Att->getModule() != "malloc") + return; + + OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + if (I != E) { + const GRState *state = + MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); + C.addTransition(state); + return; + } + const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), + C.getState()); + C.addTransition(state); +} + +const GRState *MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + SVal Size, SVal Init, + const GRState *state) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + // Set the return value. + SVal retVal = svalBuilder.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); + state = state->BindExpr(CE, retVal); + + // Fill the region with the initialization value. + state = state->bindDefault(retVal, Init); + + // Set the region's extent equal to the Size parameter. + const SymbolicRegion *R = cast<SymbolicRegion>(retVal.getAsRegion()); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal DefinedSize = cast<DefinedOrUnknownSVal>(Size); + DefinedOrUnknownSVal extentMatchesSize = + svalBuilder.evalEQ(state, Extent, DefinedSize); + + state = state->assume(extentMatchesSize, true); + assert(state); + + SymbolRef Sym = retVal.getAsLocSymbol(); + assert(Sym); + + // Set the symbol's state to Allocated. + return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE)); +} + +void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false); + + if (state) + C.addTransition(state); +} + +void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) { + if (Att->getModule() != "malloc") + return; + + for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + I != E; ++I) { + const GRState *state = FreeMemAux(C, CE, C.getState(), *I, + Att->getOwnKind() == OwnershipAttr::Holds); + if (state) + C.addTransition(state); + } +} + +const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state, unsigned Num, + bool Hold) { + const Expr *ArgExpr = CE->getArg(Num); + SVal ArgVal = state->getSVal(ArgExpr); + + DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal); + + // Check for null dereferences. + if (!isa<Loc>(location)) + return state; + + // FIXME: Technically using 'Assume' here can result in a path + // bifurcation. In such cases we need to return two states, not just one. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(location); + + // The explicit NULL case, no operation is performed. + if (nullState && !notNullState) + return nullState; + + assert(notNullState); + + // Unknown values could easily be okay + // Undefined values are handled elsewhere + if (ArgVal.isUnknownOrUndef()) + return notNullState; + + const MemRegion *R = ArgVal.getAsRegion(); + + // Nonlocs can't be freed, of course. + // Non-region locations (labels and fixed addresses) also shouldn't be freed. + if (!R) { + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + return NULL; + } + + R = R->StripCasts(); + + // Blocks might show up as heap data, but should not be free()d + if (isa<BlockDataRegion>(R)) { + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + return NULL; + } + + const MemSpaceRegion *MS = R->getMemorySpace(); + + // Parameters, locals, statics, and globals shouldn't be freed. + if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) { + // FIXME: at the time this code was written, malloc() regions were + // represented by conjured symbols, which are all in UnknownSpaceRegion. + // This means that there isn't actually anything from HeapSpaceRegion + // that should be freed, even though we allow it here. + // Of course, free() can work on memory allocated outside the current + // function, so UnknownSpaceRegion is always a possibility. + // False negatives are better than false positives. + + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + return NULL; + } + + const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); + // Various cases could lead to non-symbol values here. + // For now, ignore them. + if (!SR) + return notNullState; + + SymbolRef Sym = SR->getSymbol(); + const RefState *RS = state->get<RegionState>(Sym); + + // If the symbol has not been tracked, return. This is possible when free() is + // called on a pointer that does not get its pointee directly from malloc(). + // Full support of this requires inter-procedural analysis. + if (!RS) + return notNullState; + + // Check double free. + if (RS->isReleased()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_DoubleFree) + BT_DoubleFree + = new BuiltinBug("Double free", + "Try to free a memory block that has been released"); + // FIXME: should find where it's freed last time. + BugReport *R = new BugReport(*BT_DoubleFree, + BT_DoubleFree->getDescription(), N); + C.EmitReport(R); + } + return NULL; + } + + // Normal free. + if (Hold) + return notNullState->set<RegionState>(Sym, RefState::getRelinquished(CE)); + return notNullState->set<RegionState>(Sym, RefState::getReleased(CE)); +} + +bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) { + if (nonloc::ConcreteInt *IntVal = dyn_cast<nonloc::ConcreteInt>(&V)) + os << "an integer (" << IntVal->getValue() << ")"; + else if (loc::ConcreteInt *ConstAddr = dyn_cast<loc::ConcreteInt>(&V)) + os << "a constant address (" << ConstAddr->getValue() << ")"; + else if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&V)) + os << "the address of the label '" << Label->getLabel()->getName() << "'"; + else + return false; + + return true; +} + +bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os, + const MemRegion *MR) { + switch (MR->getKind()) { + case MemRegion::FunctionTextRegionKind: { + const FunctionDecl *FD = cast<FunctionTextRegion>(MR)->getDecl(); + if (FD) + os << "the address of the function '" << FD << "'"; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockTextRegionKind: + os << "block text"; + return true; + case MemRegion::BlockDataRegionKind: + // FIXME: where the block came from? + os << "a block"; + return true; + default: { + const MemSpaceRegion *MS = MR->getMemorySpace(); + + switch (MS->getKind()) { + case MemRegion::StackLocalsSpaceRegionKind: { + const VarRegion *VR = dyn_cast<VarRegion>(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + if (VD) + os << "the address of the local variable '" << VD->getName() << "'"; + else + os << "the address of a local stack variable"; + return true; + } + case MemRegion::StackArgumentsSpaceRegionKind: { + const VarRegion *VR = dyn_cast<VarRegion>(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + if (VD) + os << "the address of the parameter '" << VD->getName() << "'"; + else + os << "the address of a parameter"; + return true; + } + case MemRegion::NonStaticGlobalSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: { + const VarRegion *VR = dyn_cast<VarRegion>(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + if (VD) { + if (VD->isStaticLocal()) + os << "the address of the static variable '" << VD->getName() << "'"; + else + os << "the address of the global variable '" << VD->getName() << "'"; + } else + os << "the address of a global variable"; + return true; + } + default: + return false; + } + } + } +} + +void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, + SourceRange range) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_BadFree) + BT_BadFree = new BuiltinBug("Bad free"); + + llvm::SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + const MemRegion *MR = ArgVal.getAsRegion(); + if (MR) { + while (const ElementRegion *ER = dyn_cast<ElementRegion>(MR)) + MR = ER->getSuperRegion(); + + // Special case for alloca() + if (isa<AllocaRegion>(MR)) + os << "Argument to free() was allocated by alloca(), not malloc()"; + else { + os << "Argument to free() is "; + if (SummarizeRegion(os, MR)) + os << ", which is not memory allocated by malloc()"; + else + os << "not memory allocated by malloc()"; + } + } else { + os << "Argument to free() is "; + if (SummarizeValue(os, ArgVal)) + os << ", which is not memory allocated by malloc()"; + else + os << "not memory allocated by malloc()"; + } + + EnhancedBugReport *R = new EnhancedBugReport(*BT_BadFree, os.str(), N); + R->addRange(range); + C.EmitReport(R); + } +} + +void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *arg0Expr = CE->getArg(0); + DefinedOrUnknownSVal arg0Val + = cast<DefinedOrUnknownSVal>(state->getSVal(arg0Expr)); + + SValBuilder &svalBuilder = C.getSValBuilder(); + + DefinedOrUnknownSVal PtrEQ = + svalBuilder.evalEQ(state, arg0Val, svalBuilder.makeNull()); + + // If the ptr is NULL, the call is equivalent to malloc(size). + if (const GRState *stateEqual = state->assume(PtrEQ, true)) { + // Hack: set the NULL symbolic region to released to suppress false warning. + // In the future we should add more states for allocated regions, e.g., + // CheckedNull, CheckedNonNull. + + SymbolRef Sym = arg0Val.getAsLocSymbol(); + if (Sym) + stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE)); + + const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), + UndefinedVal(), stateEqual); + C.addTransition(stateMalloc); + } + + if (const GRState *stateNotEqual = state->assume(PtrEQ, false)) { + const Expr *Arg1 = CE->getArg(1); + DefinedOrUnknownSVal Arg1Val = + cast<DefinedOrUnknownSVal>(stateNotEqual->getSVal(Arg1)); + DefinedOrUnknownSVal SizeZero = + svalBuilder.evalEQ(stateNotEqual, Arg1Val, + svalBuilder.makeIntValWithPtrWidth(0, false)); + + if (const GRState *stateSizeZero = stateNotEqual->assume(SizeZero, true)) + if (const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero, 0, false)) + C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); + + if (const GRState *stateSizeNotZero = stateNotEqual->assume(SizeZero,false)) + if (const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, + 0, false)) { + // FIXME: We should copy the content of the original buffer. + const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), + UnknownVal(), stateFree); + C.addTransition(stateRealloc); + } + } +} + +void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + SVal count = state->getSVal(CE->getArg(0)); + SVal elementSize = state->getSVal(CE->getArg(1)); + SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize, + svalBuilder.getContext().getSizeType()); + SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); + + C.addTransition(MallocMemAux(C, CE, TotalSize, zeroVal, state)); +} + +void MallocChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper) +{ + if (!SymReaper.hasDeadSymbols()) + return; + + const GRState *state = C.getState(); + RegionStateTy RS = state->get<RegionState>(); + RegionStateTy::Factory &F = state->get_context<RegionState>(); + + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (SymReaper.isDead(I->first)) { + if (I->second.isAllocated()) { + if (ExplodedNode *N = C.generateNode()) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + // FIXME: where it is allocated. + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + C.EmitReport(R); + } + } + + // Remove the dead symbol from the map. + RS = F.remove(RS, I->first); + } + } + C.generateNode(state->set<RegionState>(RS)); +} + +void MallocChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, + ExprEngine &Eng) { + const GRState *state = B.getState(); + RegionStateTy M = state->get<RegionState>(); + + for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { + RefState RS = I->second; + if (RS.isAllocated()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} + +void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *retExpr = S->getRetValue(); + if (!retExpr) + return; + + const GRState *state = C.getState(); + + SymbolRef Sym = state->getSVal(retExpr).getAsSymbol(); + if (!Sym) + return; + + const RefState *RS = state->get<RegionState>(Sym); + if (!RS) + return; + + // FIXME: check other cases. + if (RS->isAllocated()) + state = state->set<RegionState>(Sym, RefState::getEscaped(S)); + + C.addTransition(state); +} + +const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond, + bool Assumption, + bool * /* respondsToCallback */) { + // If a symblic region is assumed to NULL, set its state to AllocateFailed. + // FIXME: should also check symbols assumed to non-null. + + RegionStateTy RS = state->get<RegionState>(); + + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (state->getSymVal(I.getKey())) + state = state->set<RegionState>(I.getKey(),RefState::getAllocateFailed()); + } + + return state; +} + +// Check if the location is a freed symbolic region. +void MallocChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l, + bool isLoad) { + SymbolRef Sym = l.getLocSymbolInBase(); + if (Sym) { + const RefState *RS = C.getState()->get<RegionState>(Sym); + if (RS && RS->isReleased()) { + if (ExplodedNode *N = C.generateNode()) { + if (!BT_UseFree) + BT_UseFree = new BuiltinBug("Use dynamically allocated memory after" + " it is freed."); + + BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(), + N); + C.EmitReport(R); + } + } + } +} + +void MallocChecker::PreVisitBind(CheckerContext &C, + const Stmt *StoreE, + SVal location, + SVal val) { + // The PreVisitBind implements the same algorithm as already used by the + // Objective C ownership checker: if the pointer escaped from this scope by + // assignment, let it go. However, assigning to fields of a stack-storage + // structure does not transfer ownership. + + const GRState *state = C.getState(); + DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location); + + // Check for null dereferences. + if (!isa<Loc>(l)) + return; + + // Before checking if the state is null, check if 'val' has a RefState. + // Only then should we check for null and bifurcate the state. + SymbolRef Sym = val.getLocSymbolInBase(); + if (Sym) { + if (const RefState *RS = state->get<RegionState>(Sym)) { + // If ptr is NULL, no operation is performed. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(l); + + // Generate a transition for 'nullState' to record the assumption + // that the state was null. + if (nullState) + C.addTransition(nullState); + + if (!notNullState) + return; + + if (RS->isAllocated()) { + // Something we presently own is being assigned somewhere. + const MemRegion *AR = location.getAsRegion(); + if (!AR) + return; + AR = AR->StripCasts()->getBaseRegion(); + do { + // If it is on the stack, we still own it. + if (AR->hasStackNonParametersStorage()) + break; + + // If the state can't represent this binding, we still own it. + if (notNullState == (notNullState->bindLoc(cast<Loc>(location), + UnknownVal()))) + break; + + // We no longer own this pointer. + notNullState = + notNullState->set<RegionState>(Sym, + RefState::getRelinquished(StoreE)); + } + while (false); + } + C.addTransition(notNullState); + } + } +} diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp new file mode 100644 index 0000000..9fb89d7 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,92 @@ +//=- NSAutoreleasePoolChecker.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 a NSAutoreleasePoolChecker, a small checker that warns +// about subpar uses of NSAutoreleasePool. Note that while the check itself +// (in it's current form) could be written as a flow-insensitive check, in +// can be potentially enhanced in the future with flow-sensitive information. +// It is also a good example of the CheckerVisitor interface. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" + +using namespace clang; +using namespace ento; + +namespace { +class NSAutoreleasePoolChecker + : public CheckerVisitor<NSAutoreleasePoolChecker> { + + Selector releaseS; + +public: + NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); +}; + +} // end anonymous namespace + + +static void RegisterNSAutoreleasePoolChecker(ExprEngine &Eng) { + ASTContext &Ctx = Eng.getContext(); + if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { + Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", + Ctx))); + } +} + +void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterNSAutoreleasePoolChecker); +} + +void +NSAutoreleasePoolChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { + + const Expr *receiver = msg.getInstanceReceiver(); + if (!receiver) + return; + + // FIXME: Enhance with value-tracking information instead of consulting + // the type of the expression. + const ObjCObjectPointerType* PT = + receiver->getType()->getAs<ObjCObjectPointerType>(); + + if (!PT) + return; + const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); + if (!OD) + return; + if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + return; + + // Sending 'release' message? + if (msg.getSelector() != releaseS) + return; + + SourceRange R = msg.getSourceRange(); + + C.getBugReporter().EmitBasicReport("Use -drain instead of -release", + "API Upgrade (Apple)", + "Use -drain instead of -release when using NSAutoreleasePool " + "and garbage collection", R.getBegin(), &R, 1); +} diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp new file mode 100644 index 0000000..7a1b978 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -0,0 +1,238 @@ +//=- NSErrorCheckerer.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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckNSError, a flow-insenstive check +// that determines if an Objective-C class interface correctly returns +// a non-void return type. +// +// File under feature request PR 2600. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "BasicObjCFoundationChecks.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; +using namespace ento; + +namespace { +class NSErrorChecker : public BugType { + const Decl &CodeDecl; + const bool isNSErrorWarning; + IdentifierInfo * const II; + ExprEngine &Eng; + + void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams); + + void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams); + + bool CheckNSErrorArgument(QualType ArgTy); + bool CheckCFErrorArgument(QualType ArgTy); + + void CheckParamDeref(const VarDecl *V, const LocationContext *LC, + const GRState *state, BugReporter& BR); + + void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); + +public: + NSErrorChecker(const Decl &D, bool isNSError, ExprEngine& eng) + : BugType(isNSError ? "NSError** null dereference" + : "CFErrorRef* null dereference", + "Coding conventions (Apple)"), + CodeDecl(D), + isNSErrorWarning(isNSError), + II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), + Eng(eng) {} + + void FlushReports(BugReporter& BR); +}; + +} // end anonymous namespace + +void ento::RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, + const Decl &D) { + BR.Register(new NSErrorChecker(D, true, Eng)); + BR.Register(new NSErrorChecker(D, false, Eng)); +} + +void NSErrorChecker::FlushReports(BugReporter& BR) { + // Get the analysis engine and the exploded analysis graph. + ExplodedGraph& G = Eng.getGraph(); + + // Get the ASTContext, which is useful for querying type information. + ASTContext &Ctx = BR.getContext(); + + QualType ResultTy; + llvm::SmallVector<VarDecl*, 5> ErrorParams; + + if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl)) + CheckSignature(*MD, ResultTy, ErrorParams); + else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl)) + CheckSignature(*FD, ResultTy, ErrorParams); + else + return; + + if (ErrorParams.empty()) + return; + + if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); + + for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); + RI!=RE; ++RI) { + // Scan the parameters for an implicit null dereference. + for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(), + E=ErrorParams.end(); I!=E; ++I) + CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); + } +} + +void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (isa<ObjCMethodDecl>(CodeDecl)) + os << "Method"; + else + os << "Function"; + + os << " accepting "; + os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); + os << " should have a non-void return value to indicate whether or not an " + "error occurred"; + + BR.EmitBasicReport(isNSErrorWarning + ? "Bad return type when passing NSError**" + : "Bad return type when passing CFError*", + getCategory(), os.str(), + CodeDecl.getLocation()); +} + +void +NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { + + ResultTy = M.getResultType(); + + for (ObjCMethodDecl::param_iterator I=M.param_begin(), + E=M.param_end(); I!=E; ++I) { + + QualType T = (*I)->getType(); + + if (isNSErrorWarning) { + if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); + } + else if (CheckCFErrorArgument(T)) + ErrorParams.push_back(*I); + } +} + +void +NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { + + ResultTy = F.getResultType(); + + for (FunctionDecl::param_const_iterator I = F.param_begin(), + E = F.param_end(); I != E; ++I) { + + QualType T = (*I)->getType(); + + if (isNSErrorWarning) { + if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); + } + else if (CheckCFErrorArgument(T)) + ErrorParams.push_back(*I); + } +} + + +bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAs<PointerType>(); + if (!PPT) + return false; + + const ObjCObjectPointerType* PT = + PPT->getPointeeType()->getAs<ObjCObjectPointerType>(); + + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + + // FIXME: Can ID ever be NULL? + if (ID) + return II == ID->getIdentifier(); + + return false; +} + +bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAs<PointerType>(); + if (!PPT) return false; + + const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>(); + if (!TT) return false; + + return TT->getDecl()->getIdentifier() == II; +} + +void NSErrorChecker::CheckParamDeref(const VarDecl *Param, + const LocationContext *LC, + const GRState *rootState, + BugReporter& BR) { + + SVal ParamL = rootState->getLValue(Param, LC); + const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>(); + assert (ParamR && "Parameters always have VarRegions."); + SVal ParamSVal = rootState->getSVal(ParamR); + + // FIXME: For now assume that ParamSVal is symbolic. We need to generalize + // this later. + SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); + if (!ParamSym) + return; + + // Iterate over the implicit-null dereferences. + ExplodedNode *const* I, *const* E; + llvm::tie(I, E) = GetImplicitNullDereferences(Eng); + for ( ; I != E; ++I) { + const GRState *state = (*I)->getState(); + SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt()); + if (location.getAsSymbol() != ParamSym) + continue; + + // Emit an error. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Potential null dereference. According to coding standards "; + + if (isNSErrorWarning) + os << "in 'Creating and Returning NSError Objects' the parameter '"; + else + os << "documented in CoreFoundation/CFError.h the parameter '"; + + os << Param << "' may be null."; + + BugReport *report = new BugReport(*this, os.str(), *I); + // FIXME: Notable symbols are now part of the report. We should + // add support for notable symbols in BugReport. + // BR.addNotableSymbol(SV->getSymbol()); + BR.EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp new file mode 100644 index 0000000..40040ea --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -0,0 +1,80 @@ +//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NoReturnFunctionChecker, which evaluates functions that do not +// return to the caller. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace ento; + +namespace { + +class NoReturnFunctionChecker : public CheckerVisitor<NoReturnFunctionChecker> { +public: + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void ento::RegisterNoReturnFunctionChecker(ExprEngine &Eng) { + Eng.registerCheck(new NoReturnFunctionChecker()); +} + +void NoReturnFunctionChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + + bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + + if (!BuildSinks) { + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return; + + if (FD->getAttr<AnalyzerNoReturnAttr>()) + BuildSinks = true; + else if (const IdentifierInfo *II = FD->getIdentifier()) { + // HACK: Some functions are not marked noreturn, and don't return. + // Here are a few hardwired ones. If this takes too long, we can + // potentially cache these results. + BuildSinks + = llvm::StringSwitch<bool>(llvm::StringRef(II->getName())) + .Case("exit", true) + .Case("panic", true) + .Case("error", true) + .Case("Assert", true) + // FIXME: This is just a wrapper around throwing an exception. + // Eventually inter-procedural analysis should handle this easily. + .Case("ziperr", true) + .Case("assfail", true) + .Case("db_error", true) + .Case("__assert", true) + .Case("__assert_rtn", true) + .Case("__assert_fail", true) + .Case("dtrace_assfail", true) + .Case("yy_fatal_error", true) + .Case("_XCAssertionFailureHandler", true) + .Case("_DTAssertionFailureHandler", true) + .Case("_TSAssertionFailureHandler", true) + .Default(false); + } + } + + if (BuildSinks) + C.generateSink(CE); +} diff --git a/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp new file mode 100644 index 0000000..e1126b6 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp @@ -0,0 +1,203 @@ +//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- 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 OSAtomic functions. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { + +class OSAtomicChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + +private: + bool evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); +}; + +} + +void ento::RegisterOSAtomicChecker(ExprEngine &Eng) { + Eng.registerCheck(new OSAtomicChecker()); +} + +bool OSAtomicChecker::evalCallExpr(CheckerContext &C,const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl* FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + + llvm::StringRef FName(II->getName()); + + // Check for compare and swap. + if (FName.startswith("OSAtomicCompareAndSwap") || + FName.startswith("objc_atomicCompareAndSwap")) + return evalOSAtomicCompareAndSwap(C, CE); + + // FIXME: Other atomics. + return false; +} + +bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, + const CallExpr *CE) { + // Not enough arguments to match OSAtomicCompareAndSwap? + if (CE->getNumArgs() != 3) + return false; + + ASTContext &Ctx = C.getASTContext(); + const Expr *oldValueExpr = CE->getArg(0); + QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); + + const Expr *newValueExpr = CE->getArg(1); + QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); + + // Do the types of 'oldValue' and 'newValue' match? + if (oldValueType != newValueType) + return false; + + const Expr *theValueExpr = CE->getArg(2); + const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>(); + + // theValueType not a pointer? + if (!theValueType) + return false; + + QualType theValueTypePointee = + Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); + + // The pointee must match newValueType and oldValueType. + if (theValueTypePointee != newValueType) + return false; + + static unsigned magic_load = 0; + static unsigned magic_store = 0; + + const void *OSAtomicLoadTag = &magic_load; + const void *OSAtomicStoreTag = &magic_store; + + // Load 'theValue'. + ExprEngine &Engine = C.getEngine(); + const GRState *state = C.getState(); + ExplodedNodeSet Tmp; + SVal location = state->getSVal(theValueExpr); + // Here we should use the value type of the region as the load type, because + // we are simulating the semantics of the function, not the semantics of + // passing argument. So the type of theValue expr is not we are loading. + // But usually the type of the varregion is not the type we want either, + // we still need to do a CastRetrievedVal in store manager. So actually this + // LoadTy specifying can be omitted. But we put it here to emphasize the + // semantics. + QualType LoadTy; + if (const TypedRegion *TR = + dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + LoadTy = TR->getValueType(); + } + Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(), + state, location, OSAtomicLoadTag, LoadTy); + + if (Tmp.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode *N = *I; + const GRState *stateLoad = N->getState(); + SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); + SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); + + // FIXME: Issue an error. + if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { + return false; + } + + DefinedOrUnknownSVal theValueVal = + cast<DefinedOrUnknownSVal>(theValueVal_untested); + DefinedOrUnknownSVal oldValueVal = + cast<DefinedOrUnknownSVal>(oldValueVal_untested); + + SValBuilder &svalBuilder = Engine.getSValBuilder(); + + // Perform the comparison. + DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); + + const GRState *stateEqual = stateLoad->assume(Cmp, true); + + // Were they equal? + if (stateEqual) { + // Perform the store. + ExplodedNodeSet TmpStore; + SVal val = stateEqual->getSVal(newValueExpr); + + // Handle implicit value casts. + if (const TypedRegion *R = + dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); + } + + Engine.evalStore(TmpStore, NULL, theValueExpr, N, + stateEqual, location, val, OSAtomicStoreTag); + + if (TmpStore.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + // Now bind the result of the comparison. + for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), + E2 = TmpStore.end(); I2 != E2; ++I2) { + ExplodedNode *predNew = *I2; + const GRState *stateNew = predNew->getState(); + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Engine.getSValBuilder().makeTruthVal(true, T); + C.generateNode(stateNew->BindExpr(CE, Res), predNew); + } + } + + // Were they not equal? + if (const GRState *stateNotEqual = stateLoad->assume(Cmp, false)) { + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType()); + C.generateNode(stateNotEqual->BindExpr(CE, Res), N); + } + } + + return true; +} diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp new file mode 100644 index 0000000..7d440ab --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -0,0 +1,100 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCAtSyncChecker, a builtin check that checks for null pointers +// used as mutexes for @synchronized. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCAtSyncChecker : public CheckerVisitor<ObjCAtSyncChecker> { + BuiltinBug *BT_null; + BuiltinBug *BT_undef; +public: + ObjCAtSyncChecker() : BT_null(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PreVisitObjCAtSynchronizedStmt(CheckerContext &C, + const ObjCAtSynchronizedStmt *S); +}; +} // end anonymous namespace + +static void RegisterObjCAtSyncChecker(ExprEngine &Eng) { + // @synchronized is an Objective-C 2 feature. + if (Eng.getContext().getLangOptions().ObjC2) + Eng.registerCheck(new ObjCAtSyncChecker()); +} + +void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterObjCAtSyncChecker); +} + +void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, + const ObjCAtSynchronizedStmt *S) { + + const Expr *Ex = S->getSynchExpr(); + const GRState *state = C.getState(); + SVal V = state->getSVal(Ex); + + // Uninitialized value used for the mutex? + if (isa<UndefinedVal>(V)) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_undef) + BT_undef = new BuiltinBug("Uninitialized value used as mutex " + "for @synchronized"); + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + C.EmitReport(report); + } + return; + } + + if (V.isUnknown()) + return; + + // Check for null mutexes. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); + + if (nullState) { + if (!notNullState) { + // Generate an error node. This isn't a sink since + // a null mutex just means no synchronization occurs. + if (ExplodedNode *N = C.generateNode(nullState)) { + if (!BT_null) + BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() " + "(no synchronization will occur)"); + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + Ex); + + C.EmitReport(report); + return; + } + } + // Don't add a transition for 'nullState'. If the value is + // under-constrained to be null or non-null, assume it is non-null + // afterwards. + } + + if (notNullState) + C.addTransition(notNullState); +} + diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp new file mode 100644 index 0000000..4f247ea --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -0,0 +1,356 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCSelfInitChecker, a builtin check that checks for uses of +// 'self' before proper initialization. +// +//===----------------------------------------------------------------------===// + +// This checks initialization methods to verify that they assign 'self' to the +// result of an initialization call (e.g. [super init], or [self initWith..]) +// before using 'self' or any instance variable. +// +// To perform the required checking, values are tagged wih flags that indicate +// 1) if the object is the one pointed to by 'self', and 2) if the object +// is the result of an initializer (e.g. [super init]). +// +// Uses of an object that is true for 1) but not 2) trigger a diagnostic. +// The uses that are currently checked are: +// - Using instance variables. +// - Returning the object. +// +// Note that we don't check for an invalid 'self' that is the receiver of an +// obj-c message expression to cut down false positives where logging functions +// get information from self (like its class) or doing "invalidation" on self +// when the initialization fails. +// +// Because the object that 'self' points to gets invalidated when a call +// receives a reference to 'self', the checker keeps track and passes the flags +// for 1) and 2) to the new object that 'self' points to after the call. +// +// FIXME (rdar://7937506): In the case of: +// [super init]; +// return self; +// Have an extra PathDiagnosticPiece in the path that says "called [super init], +// but didn't assign the result to self." + +//===----------------------------------------------------------------------===// + +// FIXME: Somehow stick the link to Apple's documentation about initializing +// objects in the diagnostics. +// http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace ento; + +static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); +static bool isInitializationMethod(const ObjCMethodDecl *MD); +static bool isInitMessage(const ObjCMessage &msg); +static bool isSelfVar(SVal location, CheckerContext &C); + +namespace { +enum SelfFlagEnum { + /// \brief No flag set. + SelfFlag_None = 0x0, + /// \brief Value came from 'self'. + SelfFlag_Self = 0x1, + /// \brief Value came from the result of an initializer (e.g. [super init]). + SelfFlag_InitRes = 0x2 +}; +} + +namespace { +class ObjCSelfInitChecker : public CheckerVisitor<ObjCSelfInitChecker> { + /// \brief A call receiving a reference to 'self' invalidates the object that + /// 'self' contains. This field keeps the "self flags" assigned to the 'self' + /// object before the call and assign them to the new object that 'self' + /// points to after the call. + SelfFlagEnum preCallSelfFlags; + +public: + static void *getTag() { static int tag = 0; return &tag; } + void postVisitObjCMessage(CheckerContext &C, ObjCMessage msg); + void PostVisitObjCIvarRefExpr(CheckerContext &C, const ObjCIvarRefExpr *E); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + void PreVisitGenericCall(CheckerContext &C, const CallExpr *CE); + void PostVisitGenericCall(CheckerContext &C, const CallExpr *CE); + virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location, + bool isLoad); +}; +} // end anonymous namespace + +static void RegisterObjCSelfInitChecker(ExprEngine &Eng) { + if (Eng.getContext().getLangOptions().ObjC1) + Eng.registerCheck(new ObjCSelfInitChecker()); +} + +void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterObjCSelfInitChecker); +} + +namespace { + +class InitSelfBug : public BugType { + const std::string desc; +public: + InitSelfBug() : BugType("missing \"self = [(super or self) init...]\"", + "missing \"self = [(super or self) init...]\"") {} +}; + +} // end anonymous namespace + +typedef llvm::ImmutableMap<SymbolRef, unsigned> SelfFlag; +namespace { struct CalledInit {}; } + +namespace clang { +namespace ento { + template<> + struct GRStateTrait<SelfFlag> : public GRStatePartialTrait<SelfFlag> { + static void* GDMIndex() { + static int index = 0; + return &index; + } + }; + template <> + struct GRStateTrait<CalledInit> : public GRStatePartialTrait<bool> { + static void *GDMIndex() { static int index = 0; return &index; } + }; +} +} + +static SelfFlagEnum getSelfFlags(SVal val, const GRState *state) { + if (SymbolRef sym = val.getAsSymbol()) + if (const unsigned *attachedFlags = state->get<SelfFlag>(sym)) + return (SelfFlagEnum)*attachedFlags; + return SelfFlag_None; +} + +static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { + return getSelfFlags(val, C.getState()); +} + +static void addSelfFlag(const GRState *state, SVal val, + SelfFlagEnum flag, CheckerContext &C) { + // We tag the symbol that the SVal wraps. + if (SymbolRef sym = val.getAsSymbol()) + C.addTransition(state->set<SelfFlag>(sym, getSelfFlags(val, C) | flag)); +} + +static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { + return getSelfFlags(val, C) & flag; +} + +/// \brief Returns true of the value of the expression is the object that 'self' +/// points to and is an object that did not come from the result of calling +/// an initializer. +static bool isInvalidSelf(const Expr *E, CheckerContext &C) { + SVal exprVal = C.getState()->getSVal(E); + if (!hasSelfFlag(exprVal, SelfFlag_Self, C)) + return false; // value did not come from 'self'. + if (hasSelfFlag(exprVal, SelfFlag_InitRes, C)) + return false; // 'self' is properly initialized. + + return true; +} + +static void checkForInvalidSelf(const Expr *E, CheckerContext &C, + const char *errorStr) { + if (!E) + return; + + if (!C.getState()->get<CalledInit>()) + return; + + if (!isInvalidSelf(E, C)) + return; + + // Generate an error node. + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + EnhancedBugReport *report = + new EnhancedBugReport(*new InitSelfBug(), errorStr, N); + C.EmitReport(report); +} + +void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { + // When encountering a message that does initialization (init rule), + // tag the return value so that we know later on that if self has this value + // then it is properly initialized. + + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisContext()->getDecl()))) + return; + + if (isInitMessage(msg)) { + // Tag the return value as the result of an initializer. + const GRState *state = C.getState(); + + // FIXME this really should be context sensitive, where we record + // the current stack frame (for IPA). Also, we need to clean this + // value out when we return from this method. + state = state->set<CalledInit>(true); + + SVal V = state->getSVal(msg.getOriginExpr()); + addSelfFlag(state, V, SelfFlag_InitRes, C); + return; + } + + // We don't check for an invalid 'self' in an obj-c message expression to cut + // down false positives where logging functions get information from self + // (like its class) or doing "invalidation" on self when the initialization + // fails. +} + +void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext &C, + const ObjCIvarRefExpr *E) { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisContext()->getDecl()))) + return; + + checkForInvalidSelf(E->getBase(), C, + "Instance variable used while 'self' is not set to the result of " + "'[(super or self) init...]'"); +} + +void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *S) { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisContext()->getDecl()))) + return; + + checkForInvalidSelf(S->getRetValue(), C, + "Returning 'self' while it is not set to the result of " + "'[(super or self) init...]'"); +} + +// When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass +// the SelfFlags from the object 'self' point to before the call, to the new +// object after the call. This is to avoid invalidation of 'self' by logging +// functions. +// Another common pattern in classes with multiple initializers is to put the +// subclass's common initialization bits into a static function that receives +// the value of 'self', e.g: +// @code +// if (!(self = [super init])) +// return nil; +// if (!(self = _commonInit(self))) +// return nil; +// @endcode +// Until we can use inter-procedural analysis, in such a call, transfer the +// SelfFlags to the result of the call. + +void ObjCSelfInitChecker::PreVisitGenericCall(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + for (CallExpr::const_arg_iterator + I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { + SVal argV = state->getSVal(*I); + if (isSelfVar(argV, C)) { + preCallSelfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); + return; + } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { + preCallSelfFlags = getSelfFlags(argV, C); + return; + } + } +} + +void ObjCSelfInitChecker::PostVisitGenericCall(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + for (CallExpr::const_arg_iterator + I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { + SVal argV = state->getSVal(*I); + if (isSelfVar(argV, C)) { + addSelfFlag(state, state->getSVal(cast<Loc>(argV)), preCallSelfFlags, C); + return; + } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { + addSelfFlag(state, state->getSVal(CE), preCallSelfFlags, C); + return; + } + } +} + +void ObjCSelfInitChecker::visitLocation(CheckerContext &C, const Stmt *S, + SVal location, bool isLoad) { + // Tag the result of a load from 'self' so that we can easily know that the + // value is the object that 'self' points to. + const GRState *state = C.getState(); + if (isSelfVar(location, C)) + addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); +} + +// FIXME: A callback should disable checkers at the start of functions. +static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { + if (!ND) + return false; + + const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND); + if (!MD) + return false; + if (!isInitializationMethod(MD)) + return false; + + // self = [super init] applies only to NSObject subclasses. + // For instance, NSProxy doesn't implement -init. + ASTContext& Ctx = MD->getASTContext(); + IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); + ObjCInterfaceDecl* ID = MD->getClassInterface()->getSuperClass(); + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + break; + } + if (!ID) + return false; + + return true; +} + +/// \brief Returns true if the location is 'self'. +static bool isSelfVar(SVal location, CheckerContext &C) { + AnalysisContext *analCtx = C.getCurrentAnalysisContext(); + if (!analCtx->getSelfDecl()) + return false; + if (!isa<loc::MemRegionVal>(location)) + return false; + + loc::MemRegionVal MRV = cast<loc::MemRegionVal>(location); + if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.getRegion())) + return (DR->getDecl() == analCtx->getSelfDecl()); + + return false; +} + +static bool isInitializationMethod(const ObjCMethodDecl *MD) { + // Init methods with prefix like '-(id)_init' are private and the requirements + // are less strict so we don't check those. + return MD->isInstanceMethod() && + cocoa::deriveNamingConvention(MD->getSelector(), + /*ignorePrefix=*/false) == cocoa::InitRule; +} + +static bool isInitMessage(const ObjCMessage &msg) { + return cocoa::deriveNamingConvention(msg.getSelector()) == cocoa::InitRule; +} diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp new file mode 100644 index 0000000..6e92498 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -0,0 +1,184 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCUnusedIvars, a checker that +// analyzes an Objective-C class's interface/implementation to determine if it +// has any ivars that are never accessed. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace ento; + +enum IVarState { Unused, Used }; +typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; + +static void Scan(IvarUsageMap& M, const Stmt* S) { + if (!S) + return; + + if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { + const ObjCIvarDecl *D = Ex->getDecl(); + IvarUsageMap::iterator I = M.find(D); + if (I != M.end()) + I->second = Used; + return; + } + + // Blocks can reference an instance variable of a class. + if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + Scan(M, BE->getBody()); + return; + } + + for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) + Scan(M, *I); +} + +static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { + if (!D) + return; + + const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); + + if (!ID) + return; + + IvarUsageMap::iterator I = M.find(ID); + if (I != M.end()) + I->second = Used; +} + +static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { + // Scan the methods for accesses. + for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) + Scan(M, (*I)->getBody()); + + if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { + // Scan for @synthesized property methods that act as setters/getters + // to an ivar. + for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), + E = ID->propimpl_end(); I!=E; ++I) + Scan(M, *I); + + // Scan the associated categories as well. + for (const ObjCCategoryDecl *CD = + ID->getClassInterface()->getCategoryList(); CD ; + CD = CD->getNextClassCategory()) { + if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) + Scan(M, CID); + } + } +} + +static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, + SourceManager &SM) { + for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); + I!=E; ++I) + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { + SourceLocation L = FD->getLocStart(); + if (SM.getFileID(L) == FID) + Scan(M, FD->getBody()); + } +} + +static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, + BugReporter &BR) { + + const ObjCInterfaceDecl* ID = D->getClassInterface(); + IvarUsageMap M; + + // Iterate over the ivars. + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), + E=ID->ivar_end(); I!=E; ++I) { + + const ObjCIvarDecl* ID = *I; + + // Ignore ivars that... + // (a) aren't private + // (b) explicitly marked unused + // (c) are iboutlets + // (d) are unnamed bitfields + if (ID->getAccessControl() != ObjCIvarDecl::Private || + ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() || + ID->getAttr<IBOutletCollectionAttr>() || + ID->isUnnamedBitfield()) + continue; + + M[ID] = Unused; + } + + if (M.empty()) + return; + + // Now scan the implementation declaration. + Scan(M, D); + + // Any potentially unused ivars? + bool hasUnused = false; + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + hasUnused = true; + break; + } + + if (!hasUnused) + return; + + // We found some potentially unused ivars. Scan the entire translation unit + // for functions inside the @implementation that reference these ivars. + // FIXME: In the future hopefully we can just use the lexical DeclContext + // to go from the ObjCImplementationDecl to the lexically "nested" + // C functions. + SourceManager &SM = BR.getSourceManager(); + Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); + + // Find ivars that are unused. + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Instance variable '" << I->first << "' in class '" << ID + << "' is never used by the methods in its @implementation " + "(although it may be used by category methods)."; + + BR.EmitBasicReport("Unused instance variable", "Optimization", + os.str(), I->first->getLocation()); + } +} + +//===----------------------------------------------------------------------===// +// ObjCUnusedIvarsChecker +//===----------------------------------------------------------------------===// + +namespace { +class ObjCUnusedIvarsChecker : public CheckerV2< + check::ASTDecl<ObjCImplementationDecl> > { +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, + BugReporter &BR) const { + checkObjCUnusedIvar(D, BR); + } +}; +} + +void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCUnusedIvarsChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp new file mode 100644 index 0000000..741e48b --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -0,0 +1,77 @@ +//=== 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. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerArithChecker, a builtin checker that checks for +// pointer arithmetic on locations other than array elements. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class PointerArithChecker + : public CheckerVisitor<PointerArithChecker> { + BuiltinBug *BT; +public: + PointerArithChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerArithChecker::getTag() { + static int x; + return &x; +} + +void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + + if (!LR || !RV.isConstant()) + return; + + // If pointer arithmetic is done on variables of non-array type, this often + // means behavior rely on memory organization, which is dangerous. + if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || + isa<CompoundLiteralRegion>(LR)) { + + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Dangerous pointer arithmetic", + "Pointer arithmetic done on non-array variables " + "means reliance on memory layout, which is " + "dangerous."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } + } +} + +static void RegisterPointerArithChecker(ExprEngine &Eng) { + Eng.registerCheck(new PointerArithChecker()); +} + +void ento::registerPointerArithChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterPointerArithChecker); +} diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp new file mode 100644 index 0000000..f28fe9a --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -0,0 +1,84 @@ +//=== 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. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerSubChecker, a builtin checker that checks for +// pointer subtractions on two pointers pointing to different memory chunks. +// This check corresponds to CWE-469. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class PointerSubChecker + : public CheckerVisitor<PointerSubChecker> { + BuiltinBug *BT; +public: + PointerSubChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerSubChecker::getTag() { + static int x; + return &x; +} + +void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // When doing pointer subtraction, if the two pointers do not point to the + // same memory chunk, emit a warning. + if (B->getOpcode() != BO_Sub) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + const MemRegion *RR = RV.getAsRegion(); + + if (!(LR && RR)) + return; + + const MemRegion *BaseLR = LR->getBaseRegion(); + const MemRegion *BaseRR = RR->getBaseRegion(); + + if (BaseLR == BaseRR) + return; + + // Allow arithmetic on different symbolic regions. + if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) + return; + + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Pointer subtraction", + "Subtraction of two pointers that do not point to " + "the same memory chunk may cause incorrect result."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } +} + +static void RegisterPointerSubChecker(ExprEngine &Eng) { + Eng.registerCheck(new PointerSubChecker()); +} + +void ento::registerPointerSubChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterPointerSubChecker); +} diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp new file mode 100644 index 0000000..34c095f --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -0,0 +1,152 @@ +//===--- PthreadLockChecker.h - 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. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with ExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "llvm/ADT/ImmutableSet.h" + +using namespace clang; +using namespace ento; + +namespace { +class PthreadLockChecker + : public CheckerVisitor<PthreadLockChecker> { + BugType *BT; +public: + PthreadLockChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + void AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock); + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock); + +}; +} // end anonymous namespace + +// GDM Entry for tracking lock state. +namespace { class LockSet {}; } +namespace clang { +namespace ento { +template <> struct GRStateTrait<LockSet> : + public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { + static void* GDMIndex() { return PthreadLockChecker::getTag(); } +}; +} // end GR namespace +} // end clang namespace + +static void RegisterPthreadLockChecker(ExprEngine &Eng) { + Eng.registerCheck(new PthreadLockChecker()); +} + +void ento::registerPthreadLockChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterPthreadLockChecker); +} + + +void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *R = + dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + + if (!R) + return; + + IdentifierInfo *II = R->getDecl()->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return; + llvm::StringRef FName = II->getName(); + + if (FName == "pthread_mutex_lock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); + } + else if (FName == "pthread_mutex_trylock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); + } + else if (FName == "pthread_mutex_unlock") { + if (CE->getNumArgs() != 1) + return; + ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); + } +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + SVal X = state->getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = cast<DefinedSVal>(X); + const GRState *lockSucc = state; + + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const GRState *lockFail; + llvm::tie(lockFail, lockSucc) = state->assume(retVal); + assert(lockFail && lockSucc); + C.addTransition(C.generateNode(CE, lockFail)); + } + else { + // Assume that the return value was 0. + lockSucc = state->assume(retVal, false); + assert(lockSucc); + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add<LockSet>(lockR); + + C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) : + C.getPredecessor()); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + // Record that the lock was released. + // FIXME: Handle unlocking locks that were never acquired. This may + // require IPA for wrappers. + const GRState *unlockState = state->remove<LockSet>(lockR); + + if (state == unlockState) + return; + + C.addTransition(C.generateNode(CE, unlockState)); +} diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp new file mode 100644 index 0000000..838a00f --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -0,0 +1,95 @@ +//== ReturnPointerRangeChecker.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 ReturnPointerRangeChecker, which is a path-sensitive check +// which looks for an out-of-bound pointer being returned to callers. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnPointerRangeChecker : + public CheckerVisitor<ReturnPointerRangeChecker> { + BuiltinBug *BT; +public: + ReturnPointerRangeChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void ento::RegisterReturnPointerRangeChecker(ExprEngine &Eng) { + Eng.registerCheck(new ReturnPointerRangeChecker()); +} + +void *ReturnPointerRangeChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + const GRState *state = C.getState(); + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = state->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); + if (!ER) + return; + + DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + // Zero index is always in bound, this also passes ElementRegions created for + // pointer casts. + if (Idx.isZeroConstant()) + return; + // FIXME: All of this out-of-bounds checking should eventually be refactored + // into a common place. + + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType()); + + const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(StOutBound); + + if (!N) + return; + + // FIXME: This bug correspond to CWE-466. Eventually we should have bug + // types explicitly reference such exploit categories (when applicable). + if (!BT) + BT = new BuiltinBug("Return of pointer value outside of expected range", + "Returned pointer value points outside the original object " + "(potential 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. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + C.EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp new file mode 100644 index 0000000..555eaf4 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -0,0 +1,69 @@ +//== ReturnUndefChecker.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 ReturnUndefChecker, which is a path-sensitive +// check which looks for undefined or garbage values being returned to the +// caller. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnUndefChecker : + public CheckerVisitor<ReturnUndefChecker> { + BuiltinBug *BT; +public: + ReturnUndefChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void ento::RegisterReturnUndefChecker(ExprEngine &Eng) { + Eng.registerCheck(new ReturnUndefChecker()); +} + +void *ReturnUndefChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + if (!C.getState()->getSVal(RetE).isUndef()) + return; + + ExplodedNode *N = C.generateSink(); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Garbage return value", + "Undefined or garbage value returned to caller"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); + + C.EmitReport(report); +} diff --git a/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp new file mode 100644 index 0000000..363f404 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp @@ -0,0 +1,210 @@ +//=== StackAddrLeakChecker.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 stack address leak checker, which checks if an invalid +// stack address is stored into a global or heap location. See CERT DCL30-C. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +using namespace clang; +using namespace ento; + +namespace { +class StackAddrLeakChecker : public CheckerVisitor<StackAddrLeakChecker> { + BuiltinBug *BT_stackleak; + BuiltinBug *BT_returnstack; + +public: + StackAddrLeakChecker() : BT_stackleak(0), BT_returnstack(0) {} + static void *getTag() { + static int x; + return &x; + } + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); + void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng); +private: + void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE); + SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R, + SourceManager &SM); +}; +} + +static void RegisterStackAddrLeakChecker(ExprEngine &Eng) { + Eng.registerCheck(new StackAddrLeakChecker()); +} + +void ento::registerStackAddrLeakChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterStackAddrLeakChecker); +} + +SourceRange StackAddrLeakChecker::GenName(llvm::raw_ostream &os, + const MemRegion *R, + SourceManager &SM) { + // Get the base region, stripping away fields and elements. + R = R->getBaseRegion(); + SourceRange range; + os << "Address of "; + + // Check if the region is a compound literal. + if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { + const CompoundLiteralExpr* CL = CR->getLiteralExpr(); + os << "stack memory associated with a compound literal " + "declared on line " + << SM.getInstantiationLineNumber(CL->getLocStart()) + << " returned to caller"; + range = CL->getSourceRange(); + } + else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { + const Expr* ARE = AR->getExpr(); + SourceLocation L = ARE->getLocStart(); + range = ARE->getSourceRange(); + os << "stack memory allocated by call to alloca() on line " + << SM.getInstantiationLineNumber(L); + } + else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + const BlockDecl *BD = BR->getCodeRegion()->getDecl(); + SourceLocation L = BD->getLocStart(); + range = BD->getSourceRange(); + os << "stack-allocated block declared on line " + << SM.getInstantiationLineNumber(L); + } + else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + os << "stack memory associated with local variable '" + << VR->getString() << '\''; + range = VR->getDecl()->getSourceRange(); + } + else { + assert(false && "Invalid region in ReturnStackAddressChecker."); + } + + return range; +} + +void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R, + const Expr *RetE) { + ExplodedNode *N = C.generateSink(); + + if (!N) + return; + + if (!BT_returnstack) + BT_returnstack=new BuiltinBug("Return of address to stack-allocated memory"); + + // Generate a report for this bug. + llvm::SmallString<512> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range = GenName(os, R, C.getSourceManager()); + os << " returned to caller"; + RangedBugReport *report = new RangedBugReport(*BT_returnstack, os.str(), N); + report->addRange(RetE->getSourceRange()); + if (range.isValid()) + report->addRange(range); + + C.EmitReport(report); +} + +void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = C.getState()->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + if (!R || !R->hasStackStorage()) + return; + + if (R->hasStackStorage()) { + EmitStackError(C, R, RetE); + return; + } +} + +void StackAddrLeakChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, + ExprEngine &Eng) { + + const GRState *state = B.getState(); + + // Iterate over all bindings to global variables and see if it contains + // a memory region in the stack space. + class CallBack : public StoreManager::BindingsHandler { + private: + const StackFrameContext *CurSFC; + public: + llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; + + CallBack(const LocationContext *LCtx) + : CurSFC(LCtx->getCurrentStackFrame()) {} + + bool HandleBinding(StoreManager &SMgr, Store store, + const MemRegion *region, SVal val) { + + if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) + return true; + + const MemRegion *vR = val.getAsRegion(); + if (!vR) + return true; + + if (const StackSpaceRegion *SSR = + dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { + // If the global variable holds a location in the current stack frame, + // record the binding to emit a warning. + if (SSR->getStackFrame() == CurSFC) + V.push_back(std::make_pair(region, vR)); + } + + return true; + } + }; + + CallBack cb(B.getPredecessor()->getLocationContext()); + state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); + + if (cb.V.empty()) + return; + + // Generate an error node. + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (!N) + return; + + if (!BT_stackleak) + BT_stackleak = + new BuiltinBug("Stack address stored into global variable", + "Stack address was saved into a global variable. " + "This is dangerous because the address will become " + "invalid after returning from the function"); + + for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { + // Generate a report for this bug. + llvm::SmallString<512> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range = GenName(os, cb.V[i].second, + Eng.getContext().getSourceManager()); + os << " is still referred to by the global variable '"; + const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); + os << VR->getDecl()->getNameAsString() + << "' upon returning to the caller. This will be a dangling reference"; + RangedBugReport *report = new RangedBugReport(*BT_stackleak, os.str(), N); + if (range.isValid()) + report->addRange(range); + + Eng.getBugReporter().EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp new file mode 100644 index 0000000..2655be2 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -0,0 +1,470 @@ +//===-- StreamChecker.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 checkers that model and check stream handling functions. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; +using namespace ento; + +namespace { + +struct StreamState { + enum Kind { Opened, Closed, OpenFailed, Escaped } K; + const Stmt *S; + + StreamState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isOpened() const { return K == Opened; } + bool isClosed() const { return K == Closed; } + //bool isOpenFailed() const { return K == OpenFailed; } + //bool isEscaped() const { return K == Escaped; } + + bool operator==(const StreamState &X) const { + return K == X.K && S == X.S; + } + + static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); } + static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); } + static StreamState getOpenFailed(const Stmt *s) { + return StreamState(OpenFailed, s); + } + static StreamState getEscaped(const Stmt *s) { + return StreamState(Escaped, s); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + +class StreamChecker : public CheckerVisitor<StreamChecker> { + IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite, + *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, + *II_clearerr, *II_feof, *II_ferror, *II_fileno; + BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak; + +public: + StreamChecker() + : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0), + II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), + II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), + BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0), + BT_ResourceLeak(0) {} + + static void *getTag() { + static int x; + return &x; + } + + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); + void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + +private: + void Fopen(CheckerContext &C, const CallExpr *CE); + void Tmpfile(CheckerContext &C, const CallExpr *CE); + void Fclose(CheckerContext &C, const CallExpr *CE); + void Fread(CheckerContext &C, const CallExpr *CE); + void Fwrite(CheckerContext &C, const CallExpr *CE); + void Fseek(CheckerContext &C, const CallExpr *CE); + void Ftell(CheckerContext &C, const CallExpr *CE); + void Rewind(CheckerContext &C, const CallExpr *CE); + void Fgetpos(CheckerContext &C, const CallExpr *CE); + void Fsetpos(CheckerContext &C, const CallExpr *CE); + void Clearerr(CheckerContext &C, const CallExpr *CE); + void Feof(CheckerContext &C, const CallExpr *CE); + void Ferror(CheckerContext &C, const CallExpr *CE); + void Fileno(CheckerContext &C, const CallExpr *CE); + + void OpenFileAux(CheckerContext &C, const CallExpr *CE); + + const GRState *CheckNullStream(SVal SV, const GRState *state, + CheckerContext &C); + const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, + CheckerContext &C); +}; + +} // end anonymous namespace + +namespace clang { +namespace ento { + template <> + struct GRStateTrait<StreamState> + : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { + static void *GDMIndex() { return StreamChecker::getTag(); } + }; +} +} + +static void RegisterStreamChecker(ExprEngine &Eng) { + Eng.registerCheck(new StreamChecker()); +} + +void ento::registerStreamChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterStreamChecker); +} + +bool StreamChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + ASTContext &Ctx = C.getASTContext(); + if (!II_fopen) + II_fopen = &Ctx.Idents.get("fopen"); + if (!II_tmpfile) + II_tmpfile = &Ctx.Idents.get("tmpfile"); + if (!II_fclose) + II_fclose = &Ctx.Idents.get("fclose"); + if (!II_fread) + II_fread = &Ctx.Idents.get("fread"); + if (!II_fwrite) + II_fwrite = &Ctx.Idents.get("fwrite"); + if (!II_fseek) + II_fseek = &Ctx.Idents.get("fseek"); + if (!II_ftell) + II_ftell = &Ctx.Idents.get("ftell"); + if (!II_rewind) + II_rewind = &Ctx.Idents.get("rewind"); + if (!II_fgetpos) + II_fgetpos = &Ctx.Idents.get("fgetpos"); + if (!II_fsetpos) + II_fsetpos = &Ctx.Idents.get("fsetpos"); + if (!II_clearerr) + II_clearerr = &Ctx.Idents.get("clearerr"); + if (!II_feof) + II_feof = &Ctx.Idents.get("feof"); + if (!II_ferror) + II_ferror = &Ctx.Idents.get("ferror"); + if (!II_fileno) + II_fileno = &Ctx.Idents.get("fileno"); + + if (FD->getIdentifier() == II_fopen) { + Fopen(C, CE); + return true; + } + if (FD->getIdentifier() == II_tmpfile) { + Tmpfile(C, CE); + return true; + } + if (FD->getIdentifier() == II_fclose) { + Fclose(C, CE); + return true; + } + if (FD->getIdentifier() == II_fread) { + Fread(C, CE); + return true; + } + if (FD->getIdentifier() == II_fwrite) { + Fwrite(C, CE); + return true; + } + if (FD->getIdentifier() == II_fseek) { + Fseek(C, CE); + return true; + } + if (FD->getIdentifier() == II_ftell) { + Ftell(C, CE); + return true; + } + if (FD->getIdentifier() == II_rewind) { + Rewind(C, CE); + return true; + } + if (FD->getIdentifier() == II_fgetpos) { + Fgetpos(C, CE); + return true; + } + if (FD->getIdentifier() == II_fsetpos) { + Fsetpos(C, CE); + return true; + } + if (FD->getIdentifier() == II_clearerr) { + Clearerr(C, CE); + return true; + } + if (FD->getIdentifier() == II_feof) { + Feof(C, CE); + return true; + } + if (FD->getIdentifier() == II_ferror) { + Ferror(C, CE); + return true; + } + if (FD->getIdentifier() == II_fileno) { + Fileno(C, CE); + return true; + } + + return false; +} + +void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) { + OpenFileAux(C, CE); +} + +void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) { + OpenFileAux(C, CE); +} + +void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedSVal RetVal = + cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count)); + state = state->BindExpr(CE, RetVal); + + ConstraintManager &CM = C.getConstraintManager(); + // Bifurcate the state into two: one with a valid FILE* pointer, the other + // with a NULL. + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); + + if (SymbolRef Sym = RetVal.getAsSymbol()) { + // if RetVal is not NULL, set the symbol's state to Opened. + stateNotNull = + stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE)); + stateNull = + stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE)); + + C.addTransition(stateNotNull); + C.addTransition(stateNull); + } +} + +void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) { + const GRState *state = CheckDoubleClose(CE, C.getState(), C); + if (state) + C.addTransition(state); +} + +void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) + return; + // Check the legality of the 'whence' argument of 'fseek'. + SVal Whence = state->getSVal(CE->getArg(2)); + const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); + + if (!CI) + return; + + int64_t x = CI->getValue().getSExtValue(); + if (x >= 0 && x <= 2) + return; + + if (ExplodedNode *N = C.generateNode(state)) { + if (!BT_illegalwhence) + BT_illegalwhence = new BuiltinBug("Illegal whence argument", + "The whence argument to fseek() should be " + "SEEK_SET, SEEK_END, or SEEK_CUR."); + BugReport *R = new BugReport(*BT_illegalwhence, + BT_illegalwhence->getDescription(), N); + C.EmitReport(R); + } +} + +void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, + CheckerContext &C) { + const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); + if (!DV) + return 0; + + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + if (!stateNotNull && stateNull) { + if (ExplodedNode *N = C.generateSink(stateNull)) { + if (!BT_nullfp) + BT_nullfp = new BuiltinBug("NULL stream pointer", + "Stream pointer might be NULL."); + BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); + C.EmitReport(R); + } + return 0; + } + return stateNotNull; +} + +const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, + const GRState *state, + CheckerContext &C) { + SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); + if (!Sym) + return state; + + const StreamState *SS = state->get<StreamState>(Sym); + + // If the file stream is not tracked, return. + if (!SS) + return state; + + // Check: Double close a File Descriptor could cause undefined behaviour. + // Conforming to man-pages + if (SS->isClosed()) { + ExplodedNode *N = C.generateSink(); + if (N) { + if (!BT_doubleclose) + BT_doubleclose = new BuiltinBug("Double fclose", + "Try to close a file Descriptor already" + " closed. Cause undefined behaviour."); + BugReport *R = new BugReport(*BT_doubleclose, + BT_doubleclose->getDescription(), N); + C.EmitReport(R); + } + return NULL; + } + + // Close the File Descriptor. + return state->set<StreamState>(Sym, StreamState::getClosed(CE)); +} + +void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + const GRState *state = C.getState(); + const StreamState *SS = state->get<StreamState>(Sym); + if (!SS) + return; + + if (SS->isOpened()) { + ExplodedNode *N = C.generateSink(); + if (N) { + if (!BT_ResourceLeak) + BT_ResourceLeak = new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak."); + BugReport *R = new BugReport(*BT_ResourceLeak, + BT_ResourceLeak->getDescription(), N); + C.EmitReport(R); + } + } + } +} + +void StreamChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, + ExprEngine &Eng) { + const GRState *state = B.getState(); + typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; + SymMap M = state->get<StreamState>(); + + for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + StreamState SS = I->second; + if (SS.isOpened()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_ResourceLeak) + BT_ResourceLeak = new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak."); + BugReport *R = new BugReport(*BT_ResourceLeak, + BT_ResourceLeak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} + +void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = C.getState(); + SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); + + if (!Sym) + return; + + const StreamState *SS = state->get<StreamState>(Sym); + if(!SS) + return; + + if (SS->isOpened()) + state = state->set<StreamState>(Sym, StreamState::getEscaped(S)); + + C.addTransition(state); +} diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp new file mode 100644 index 0000000..14ae9ed --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -0,0 +1,120 @@ +//=== UndefBranchChecker.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 UndefBranchChecker, which checks for undefined branch +// condition. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h" + +using namespace clang; +using namespace ento; + +namespace { + +class UndefBranchChecker : public Checker { + BuiltinBug *BT; + + struct FindUndefExpr { + GRStateManager& VM; + const GRState* St; + + FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} + + const Expr* FindExpr(const Expr* Ex) { + if (!MatchesCriteria(Ex)) + return 0; + + for (Stmt::const_child_iterator I = Ex->child_begin(), + E = Ex->child_end();I!=E;++I) + if (const Expr* ExI = dyn_cast_or_null<Expr>(*I)) { + const Expr* E2 = FindExpr(ExI); + if (E2) return E2; + } + + return Ex; + } + + bool MatchesCriteria(const Expr* Ex) { return St->getSVal(Ex).isUndef(); } + }; + +public: + UndefBranchChecker() : BT(0) {} + static void *getTag(); + void VisitBranchCondition(BranchNodeBuilder &Builder, ExprEngine &Eng, + const Stmt *Condition, void *tag); +}; + +} + +void ento::RegisterUndefBranchChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefBranchChecker()); +} + +void *UndefBranchChecker::getTag() { + static int x; + return &x; +} + +void UndefBranchChecker::VisitBranchCondition(BranchNodeBuilder &Builder, + ExprEngine &Eng, + const Stmt *Condition, void *tag){ + const GRState *state = Builder.getState(); + SVal X = state->getSVal(Condition); + if (X.isUndef()) { + ExplodedNode *N = Builder.generateNode(state, true); + if (N) { + N->markAsSink(); + if (!BT) + BT = new BuiltinBug("Branch condition evaluates to a garbage value"); + + // What's going on here: we want to highlight the subexpression of the + // condition that is the most likely source of the "uninitialized + // branch condition." We do a recursive walk of the condition's + // subexpressions and roughly look for the most nested subexpression + // that binds to Undefined. We then highlight that expression's range. + BlockEdge B = cast<BlockEdge>(N->getLocation()); + const Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); + assert (Ex && "Block must have a terminator."); + + // Get the predecessor node and check if is a PostStmt with the Stmt + // being the terminator condition. We want to inspect the state + // of that node instead because it will contain main information about + // the subexpressions. + assert (!N->pred_empty()); + + // Note: any predecessor will do. They should have identical state, + // since all the BlockEdge did was act as an error sink since the value + // had to already be undefined. + ExplodedNode *PrevN = *N->pred_begin(); + ProgramPoint P = PrevN->getLocation(); + const GRState* St = N->getState(); + + if (PostStmt* PS = dyn_cast<PostStmt>(&P)) + if (PS->getStmt() == Ex) + St = PrevN->getState(); + + FindUndefExpr FindIt(Eng.getStateManager(), St); + Ex = FindIt.FindExpr(Ex); + + // Emit the bug report. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + R->addRange(Ex->getSourceRange()); + + Eng.getBugReporter().EmitReport(R); + } + + Builder.markInfeasible(true); + Builder.markInfeasible(false); + } +} diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp new file mode 100644 index 0000000..6d3c966 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -0,0 +1,102 @@ +// 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. +// +//===----------------------------------------------------------------------===// +// +// This checker detects blocks that capture uninitialized values. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefCapturedBlockVarChecker + : public CheckerVisitor<UndefCapturedBlockVarChecker> { + BugType *BT; + +public: + UndefCapturedBlockVarChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); +}; +} // end anonymous namespace + +void ento::RegisterUndefCapturedBlockVarChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefCapturedBlockVarChecker()); +} + +static const BlockDeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, + const VarDecl *VD){ + if (const BlockDeclRefExpr *BR = dyn_cast<BlockDeclRefExpr>(S)) + if (BR->getDecl() == VD) + return BR; + + for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); + I!=E; ++I) + if (const Stmt *child = *I) { + const BlockDeclRefExpr *BR = FindBlockDeclRefExpr(child, VD); + if (BR) + return BR; + } + + return NULL; +} + +void +UndefCapturedBlockVarChecker::PostVisitBlockExpr(CheckerContext &C, + const BlockExpr *BE) { + if (!BE->getBlockDecl()->hasCaptures()) + return; + + const GRState *state = C.getState(); + const BlockDataRegion *R = + cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + for (; I != E; ++I) { + // This VarRegion is the region associated with the block; we need + // the one associated with the encompassing context. + const VarRegion *VR = *I; + const VarDecl *VD = VR->getDecl(); + + if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) + continue; + + // Get the VarRegion associated with VD in the local stack frame. + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); + + if (state->getSVal(VR).isUndef()) + if (ExplodedNode *N = C.generateSink()) { + if (!BT) + BT = new BuiltinBug("uninitialized variable captured by block"); + + // Generate a bug report. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + + os << "Variable '" << VD->getName() + << "' is uninitialized when captured by block"; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) + R->addRange(Ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerFindLastStore, VR); + // need location of block + C.EmitReport(R); + } + } +} diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp new file mode 100644 index 0000000..64a3567 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -0,0 +1,87 @@ +//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefResultChecker, a builtin check in ExprEngine that +// performs checks for undefined results of non-assignment binary operators. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefResultChecker + : public CheckerVisitor<UndefResultChecker> { + + BugType *BT; + +public: + UndefResultChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void ento::RegisterUndefResultChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefResultChecker()); +} + +void UndefResultChecker::PostVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + const GRState *state = C.getState(); + if (state->getSVal(B).isUndef()) { + // Generate an error node. + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Result of operation is garbage or undefined"); + + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream OS(sbuf); + const Expr *Ex = NULL; + bool isLeft = true; + + if (state->getSVal(B->getLHS()).isUndef()) { + Ex = B->getLHS()->IgnoreParenCasts(); + isLeft = true; + } + else if (state->getSVal(B->getRHS()).isUndef()) { + Ex = B->getRHS()->IgnoreParenCasts(); + isLeft = false; + } + + if (Ex) { + OS << "The " << (isLeft ? "left" : "right") + << " operand of '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' is a garbage value"; + } + else { + // Neither operand was undefined, but the result is undefined. + OS << "The result of the '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' expression is undefined"; + } + EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); + if (Ex) { + report->addRange(Ex->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + } + else + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); + C.EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp new file mode 100644 index 0000000..ff03448 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -0,0 +1,57 @@ +//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedArraySubscriptChecker, a builtin check in ExprEngine +// that performs checks for undefined array subscripts. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedArraySubscriptChecker + : public CheckerVisitor<UndefinedArraySubscriptChecker> { + BugType *BT; +public: + UndefinedArraySubscriptChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A); +}; +} // end anonymous namespace + +void ento::RegisterUndefinedArraySubscriptChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefinedArraySubscriptChecker()); +} + +void +UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A) { + if (C.getState()->getSVal(A->getIdx()).isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT) + BT = new BuiltinBug("Array subscript is undefined"); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addRange(A->getIdx()->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + A->getIdx()); + C.EmitReport(R); + } + } +} diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp new file mode 100644 index 0000000..e53cbba --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -0,0 +1,94 @@ +//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedAssginmentChecker, a builtin check in ExprEngine that +// checks for assigning undefined values. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedAssignmentChecker + : public CheckerVisitor<UndefinedAssignmentChecker> { + BugType *BT; +public: + UndefinedAssignmentChecker() : BT(0) {} + static void *getTag(); + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val); +}; +} + +void ento::RegisterUndefinedAssignmentChecker(ExprEngine &Eng){ + Eng.registerCheck(new UndefinedAssignmentChecker()); +} + +void *UndefinedAssignmentChecker::getTag() { + static int x = 0; + return &x; +} + +void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, + const Stmt *StoreE, + SVal location, + SVal val) { + if (!val.isUndef()) + return; + + ExplodedNode *N = C.generateSink(); + + if (!N) + return; + + const char *str = "Assigned value is garbage or undefined"; + + if (!BT) + BT = new BuiltinBug(str); + + // Generate a report for this bug. + const Expr *ex = 0; + + while (StoreE) { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { + if (B->isCompoundAssignmentOp()) { + const GRState *state = C.getState(); + if (state->getSVal(B->getLHS()).isUndef()) { + str = "The left expression of the compound assignment is an " + "uninitialized value. The computed value will also be garbage"; + ex = B->getLHS(); + break; + } + } + + ex = B->getRHS(); + break; + } + + if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { + const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + ex = VD->getInit(); + } + + break; + } + + EnhancedBugReport *R = new EnhancedBugReport(*BT, str, N); + if (ex) { + R->addRange(ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); + } + C.EmitReport(R); +} + diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp new file mode 100644 index 0000000..a53ebb5 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -0,0 +1,282 @@ +//= 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. +// +//===----------------------------------------------------------------------===// +// +// This defines UnixAPIChecker, which is an assortment of checks on calls +// to various, widely used UNIX/Posix functions. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include <fcntl.h> + +using namespace clang; +using namespace ento; +using llvm::Optional; + +namespace { +class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> { + enum SubChecks { + OpenFn = 0, + PthreadOnceFn = 1, + MallocZero = 2, + NumChecks + }; + + BugType *BTypes[NumChecks]; + +public: + Optional<uint64_t> Val_O_CREAT; + +public: + UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } + static void *getTag() { static unsigned tag = 0; return &tag; } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} //end anonymous namespace + +static void RegisterUnixAPIChecker(ExprEngine &Eng) { + Eng.registerCheck(new UnixAPIChecker()); +} + +void ento::registerUnixAPIChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterUnixAPIChecker); +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static inline void LazyInitialize(BugType *&BT, const char *name) { + if (BT) + return; + BT = new BugType(name, "Unix API"); +} + +//===----------------------------------------------------------------------===// +// "open" (man 2 open) +//===----------------------------------------------------------------------===// + +static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC, + const CallExpr *CE, BugType *&BT) { + // The definition of O_CREAT is platform specific. We need a better way + // of querying this information from the checking environment. + if (!UC.Val_O_CREAT.hasValue()) { + if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) + UC.Val_O_CREAT = 0x0200; + else { + // FIXME: We need a more general way of getting the O_CREAT value. + // We could possibly grovel through the preprocessor state, but + // that would require passing the Preprocessor object to the ExprEngine. + return; + } + } + + LazyInitialize(BT, "Improper use of 'open'"); + + // Look at the 'oflags' argument for the O_CREAT flag. + const GRState *state = C.getState(); + + if (CE->getNumArgs() < 2) { + // The frontend should issue a warning for this case, so this is a sanity + // check. + return; + } + + // Now check if oflags has O_CREAT set. + const Expr *oflagsEx = CE->getArg(1); + const SVal V = state->getSVal(oflagsEx); + if (!isa<NonLoc>(V)) { + // The case where 'V' can be a location can only be due to a bad header, + // so in this case bail out. + return; + } + NonLoc oflags = cast<NonLoc>(V); + NonLoc ocreateFlag = + cast<NonLoc>(C.getSValBuilder().makeIntVal(UC.Val_O_CREAT.getValue(), + oflagsEx->getType())); + SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, + oflags, ocreateFlag, + oflagsEx->getType()); + if (maskedFlagsUC.isUnknownOrUndef()) + return; + DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); + + // Check if maskedFlags is non-zero. + const GRState *trueState, *falseState; + llvm::tie(trueState, falseState) = state->assume(maskedFlags); + + // Only emit an error if the value of 'maskedFlags' is properly + // constrained; + if (!(trueState && !falseState)) + return; + + if (CE->getNumArgs() < 3) { + ExplodedNode *N = C.generateSink(trueState); + if (!N) + return; + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, + "Call to 'open' requires a third argument when " + "the 'O_CREAT' flag is set", N); + report->addRange(oflagsEx->getSourceRange()); + C.EmitReport(report); + } +} + +//===----------------------------------------------------------------------===// +// pthread_once +//===----------------------------------------------------------------------===// + +static void CheckPthreadOnce(CheckerContext &C, UnixAPIChecker &, + const CallExpr *CE, BugType *&BT) { + + // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. + // They can possibly be refactored. + + LazyInitialize(BT, "Improper use of 'pthread_once'"); + + if (CE->getNumArgs() < 1) + return; + + // Check if the first argument is stack allocated. If so, issue a warning + // because that's likely to be bad news. + const GRState *state = C.getState(); + const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); + if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) + return; + + ExplodedNode *N = C.generateSink(state); + if (!N) + return; + + llvm::SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to 'pthread_once' uses"; + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) + os << " the local variable '" << VR->getDecl()->getName() << '\''; + else + os << " stack allocated memory"; + os << " for the \"control\" value. Using such transient memory for " + "the control value is potentially dangerous."; + if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) + os << " Perhaps you intended to declare the variable as 'static'?"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// "malloc" with allocation size 0 +//===----------------------------------------------------------------------===// + +// FIXME: Eventually this should be rolled into the MallocChecker, but this +// check is more basic and is valuable for widespread use. +static void CheckMallocZero(CheckerContext &C, UnixAPIChecker &UC, + const CallExpr *CE, BugType *&BT) { + + // Sanity check that malloc takes one argument. + if (CE->getNumArgs() != 1) + return; + + // Check if the allocation size is 0. + const GRState *state = C.getState(); + SVal argVal = state->getSVal(CE->getArg(0)); + + if (argVal.isUnknownOrUndef()) + return; + + const GRState *trueState, *falseState; + llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); + + // Is the value perfectly constrained to zero? + if (falseState && !trueState) { + ExplodedNode *N = C.generateSink(falseState); + if (!N) + return; + + // FIXME: Add reference to CERT advisory, and/or C99 standard in bug + // output. + + LazyInitialize(BT, "Undefined allocation of 0 bytes"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, "Call to 'malloc' has an allocation size" + " of 0 bytes", N); + report->addRange(CE->getArg(0)->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + CE->getArg(0)); + C.EmitReport(report); + return; + } + // Assume the the value is non-zero going forward. + assert(trueState); + if (trueState != state) { + C.addTransition(trueState); + } +} + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +typedef void (*SubChecker)(CheckerContext &C, UnixAPIChecker &UC, + const CallExpr *CE, BugType *&BT); +namespace { + class SubCheck { + SubChecker SC; + UnixAPIChecker *UC; + BugType **BT; + public: + SubCheck(SubChecker sc, UnixAPIChecker *uc, BugType *& bt) : SC(sc), UC(uc), + BT(&bt) {} + SubCheck() : SC(NULL), UC(NULL), BT(NULL) {} + + void run(CheckerContext &C, const CallExpr *CE) const { + if (SC) + SC(C, *UC, CE, *BT); + } + }; +} // end anonymous namespace + +void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + // Get the callee. All the functions we care about are C functions + // with simple identifiers. + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *Fn = + dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + + if (!Fn) + return; + + const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); + if (!FI) + return; + + const SubCheck &SC = + llvm::StringSwitch<SubCheck>(FI->getName()) + .Case("open", + SubCheck(CheckOpen, this, BTypes[OpenFn])) + .Case("pthread_once", + SubCheck(CheckPthreadOnce, this, BTypes[PthreadOnceFn])) + .Case("malloc", + SubCheck(CheckMallocZero, this, BTypes[MallocZero])) + .Default(SubCheck()); + + SC.run(C, CE); +} diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp new file mode 100644 index 0000000..3038e29 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -0,0 +1,228 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// This file implements a generalized unreachable code checker using a +// path-sensitive analysis. We mark any path visited, and then walk the CFG as a +// post-analysis to determine what was never visited. +// +// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "llvm/ADT/SmallPtrSet.h" + +// The number of CFGBlock pointers we want to reserve memory for. This is used +// once for each function we analyze. +#define DEFAULT_CFGBLOCKS 256 + +using namespace clang; +using namespace ento; + +namespace { +class UnreachableCodeChecker : public Checker { +public: + static void *getTag(); + void VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng); +private: + static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); + void FindUnreachableEntryPoints(const CFGBlock *CB); + static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); + static inline bool isEmptyCFGBlock(const CFGBlock *CB); + + llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> reachable; + llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> visited; +}; +} + +void *UnreachableCodeChecker::getTag() { + static int x = 0; + return &x; +} + +static void RegisterUnreachableCodeChecker(ExprEngine &Eng) { + Eng.registerCheck(new UnreachableCodeChecker()); +} + +void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterUnreachableCodeChecker); +} + +void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng) { + // Bail out if we didn't cover all paths + if (Eng.hasWorkRemaining()) + return; + + CFG *C = 0; + ParentMap *PM = 0; + // Iterate over ExplodedGraph + for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); + I != E; ++I) { + const ProgramPoint &P = I->getLocation(); + const LocationContext *LC = P.getLocationContext(); + + // Save the CFG if we don't have it already + if (!C) + C = LC->getAnalysisContext()->getUnoptimizedCFG(); + if (!PM) + PM = &LC->getParentMap(); + + if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB->getBlockID()); + } + } + + // Bail out if we didn't get the CFG or the ParentMap. + if (!C || !PM) + return; + + ASTContext &Ctx = B.getContext(); + + // Find CFGBlocks that were not covered by any node + for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { + const CFGBlock *CB = *I; + // Check if the block is unreachable + if (reachable.count(CB->getBlockID())) + continue; + + // Check if the block is empty (an artificial block) + if (isEmptyCFGBlock(CB)) + continue; + + // Find the entry points for this block + if (!visited.count(CB->getBlockID())) + FindUnreachableEntryPoints(CB); + + // This block may have been pruned; check if we still want to report it + if (reachable.count(CB->getBlockID())) + continue; + + // Check for false positives + if (CB->size() > 0 && isInvalidPath(CB, *PM)) + continue; + + // Special case for __builtin_unreachable. + // FIXME: This should be extended to include other unreachable markers, + // such as llvm_unreachable. + if (!CB->empty()) { + CFGElement First = CB->front(); + if (CFGStmt S = First.getAs<CFGStmt>()) { + if (const CallExpr *CE = dyn_cast<CallExpr>(S.getStmt())) { + if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) + continue; + } + } + } + + // We found a block that wasn't covered - find the statement to report + SourceRange SR; + SourceLocation SL; + if (const Stmt *S = getUnreachableStmt(CB)) { + SR = S->getSourceRange(); + SL = S->getLocStart(); + if (SR.isInvalid() || SL.isInvalid()) + continue; + } + else + continue; + + // Check if the SourceLocation is in a system header + const SourceManager &SM = B.getSourceManager(); + if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) + continue; + + B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never" + " executed", SL, SR); + } +} + +// Recursively finds the entry point(s) for this dead CFGBlock. +void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) { + visited.insert(CB->getBlockID()); + + for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); + I != E; ++I) { + if (!reachable.count((*I)->getBlockID())) { + // If we find an unreachable predecessor, mark this block as reachable so + // we don't report this block + reachable.insert(CB->getBlockID()); + if (!visited.count((*I)->getBlockID())) + // If we haven't previously visited the unreachable predecessor, recurse + FindUnreachableEntryPoints(*I); + } + } +} + +// Find the Stmt* in a CFGBlock for reporting a warning +const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { + for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { + if (CFGStmt S = I->getAs<CFGStmt>()) + return S; + } + if (const Stmt *S = CB->getTerminator()) + return S; + else + return 0; +} + +// Determines if the path to this CFGBlock contained an element that infers this +// block is a false positive. We assume that FindUnreachableEntryPoints has +// already marked only the entry points to any dead code, so we need only to +// find the condition that led to this block (the predecessor of this block.) +// There will never be more than one predecessor. +bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, + const ParentMap &PM) { + // We only expect a predecessor size of 0 or 1. If it is >1, then an external + // condition has broken our assumption (for example, a sink being placed by + // another check). In these cases, we choose not to report. + if (CB->pred_size() > 1) + return true; + + // If there are no predecessors, then this block is trivially unreachable + if (CB->pred_size() == 0) + return false; + + const CFGBlock *pred = *CB->pred_begin(); + + // Get the predecessor block's terminator conditon + const Stmt *cond = pred->getTerminatorCondition(); + + //assert(cond && "CFGBlock's predecessor has a terminator condition"); + // The previous assertion is invalid in some cases (eg do/while). Leaving + // reporting of these situations on at the moment to help triage these cases. + if (!cond) + return false; + + // Run each of the checks on the conditions + if (containsMacro(cond) || containsEnum(cond) + || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) + || containsStmt<SizeOfAlignOfExpr>(cond)) + return true; + + return false; +} + +// Returns true if the given CFGBlock is empty +bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { + return CB->getLabel() == 0 // No labels + && CB->size() == 0 // No statements + && CB->getTerminator() == 0; // No terminator +} diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp new file mode 100644 index 0000000..ba46e17 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -0,0 +1,138 @@ +//=== 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. +// +//===----------------------------------------------------------------------===// +// +// This defines VLASizeChecker, a builtin check in ExprEngine that +// performs checks for declaration of VLA of undefined or zero size. +// In addition, VLASizeChecker is responsible for defining the extent +// of the MemRegion that represents a VLA. +// +//===----------------------------------------------------------------------===// + +#include "InternalChecks.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class VLASizeChecker : public CheckerVisitor<VLASizeChecker> { + BugType *BT_zero; + BugType *BT_undef; + +public: + VLASizeChecker() : BT_zero(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); +}; +} // end anonymous namespace + +void ento::RegisterVLASizeChecker(ExprEngine &Eng) { + Eng.registerCheck(new VLASizeChecker()); +} + +void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + if (!DS->isSingleDecl()) + return; + + const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return; + + ASTContext &Ctx = C.getASTContext(); + const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); + if (!VLA) + return; + + // FIXME: Handle multi-dimensional VLAs. + const Expr* SE = VLA->getSizeExpr(); + const GRState *state = C.getState(); + SVal sizeV = state->getSVal(SE); + + if (sizeV.isUndef()) { + // Generate an error node. + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + if (!BT_undef) + BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " + "garbage value as its size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + return; + } + + // See if the size value is known. It can't be undefined because we would have + // warned about that already. + if (sizeV.isUnknown()) + return; + + // Check if the size is zero. + DefinedSVal sizeD = cast<DefinedSVal>(sizeV); + + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); + + if (stateZero && !stateNotZero) { + ExplodedNode* N = C.generateSink(stateZero); + if (!BT_zero) + BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " + "size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + return; + } + + // From this point on, assume that the size is not zero. + state = stateNotZero; + + // VLASizeChecker is responsible for defining the extent of the array being + // declared. We do this by multiplying the array length by the element size, + // then matching that with the array region's extent symbol. + + // Convert the array length to size_t. + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType SizeTy = Ctx.getSizeType(); + NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy, + SE->getType())); + + // Get the element size. + CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); + SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); + + // Multiply the array length by the element size. + SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength, + cast<NonLoc>(EleSizeVal), SizeTy); + + // Finally, assume that the array's extent matches the given size. + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + DefinedOrUnknownSVal Extent = + state->getRegion(VD, LC)->getExtent(svalBuilder); + DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); + DefinedOrUnknownSVal sizeIsKnown = + svalBuilder.evalEQ(state, Extent, ArraySize); + state = state->assume(sizeIsKnown, true); + + // Assume should not fail at this point. + assert(state); + + // Remember our assumptions! + C.addTransition(state); +} |