diff options
author | dim <dim@FreeBSD.org> | 2015-12-30 13:34:49 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2015-12-30 13:34:49 +0000 |
commit | 63b24cc778504ffd19e4c30a730e574c346312ee (patch) | |
tree | 28726ef2038e86121e353aabf52297b35a48efa2 /contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers | |
parent | 9b5bf5c4f53d65d6a48722d7410ed7cb15f5ba3a (diff) | |
parent | 3176e97f130184ece0e1a21352c8124cc83ff24a (diff) | |
download | FreeBSD-src-63b24cc778504ffd19e4c30a730e574c346312ee.zip FreeBSD-src-63b24cc778504ffd19e4c30a730e574c346312ee.tar.gz |
Update clang to trunk r256633.
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers')
67 files changed, 4802 insertions, 738 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index 166471a..a052d83 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -101,7 +101,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, else if (isa<BlockDecl>(D)) { output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); } - + NumBlocksUnreachable += unreachable; NumBlocks += total; std::string NameOfRootFunction = output.str(); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 557439b..c092610 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -23,7 +23,7 @@ using namespace clang; using namespace ento; namespace { -class ArrayBoundChecker : +class ArrayBoundChecker : public Checker<check::Location> { mutable std::unique_ptr<BuiltinBug> BT; @@ -55,17 +55,17 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, ProgramStateRef state = C.getState(); // Get the size of the array. - DefinedOrUnknownSVal NumElements - = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; - + if (!BT) BT.reset(new BuiltinBug( this, "Out-of-bound array access", @@ -82,7 +82,7 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, C.emitReport(std::move(report)); return; } - + // Array bound check succeeded. From this point forward the array bound // should always succeed. C.addTransition(StInBound); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index d8dc2aa..f4de733 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -26,15 +26,15 @@ using namespace clang; using namespace ento; namespace { -class ArrayBoundCheckerV2 : +class ArrayBoundCheckerV2 : public Checker<check::Location> { mutable std::unique_ptr<BuiltinBug> BT; enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted }; - + void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind) const; - + public: void checkLocation(SVal l, bool isLoad, const Stmt*S, CheckerContext &C) const; @@ -55,7 +55,7 @@ public: NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); } const SubRegion *getRegion() const { return baseRegion; } - + static RegionRawOffsetV2 computeOffset(ProgramStateRef state, SValBuilder &svalBuilder, SVal location); @@ -65,12 +65,12 @@ public: }; } -static SVal computeExtentBegin(SValBuilder &svalBuilder, +static SVal computeExtentBegin(SValBuilder &svalBuilder, const MemRegion *region) { while (true) switch (region->getKind()) { default: - return svalBuilder.makeZeroArrayIndex(); + return svalBuilder.makeZeroArrayIndex(); case MemRegion::SymbolicRegionKind: // FIXME: improve this later by tracking symbolic lower bounds // for symbolic regions. @@ -94,22 +94,22 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, // memory access is within the extent of the base region. Since we // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. - ProgramStateRef state = checkerContext.getState(); + ProgramStateRef state = checkerContext.getState(); ProgramStateRef originalState = state; SValBuilder &svalBuilder = checkerContext.getSValBuilder(); - const RegionRawOffsetV2 &rawOffset = + const RegionRawOffsetV2 &rawOffset = RegionRawOffsetV2::computeOffset(state, svalBuilder, location); if (!rawOffset.getRegion()) return; - // CHECK LOWER BOUND: Is byteOffset < extent begin? + // CHECK LOWER BOUND: Is byteOffset < extent begin? // If so, we are doing a load/store // before the first valid offset in the memory region. SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); - + if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), *NV, @@ -118,7 +118,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); if (!lowerBoundToCheck) return; - + ProgramStateRef state_precedesLowerBound, state_withinLowerBound; std::tie(state_precedesLowerBound, state_withinLowerBound) = state->assume(*lowerBoundToCheck); @@ -128,12 +128,12 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, 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. @@ -146,11 +146,11 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(), extentVal.castAs<NonLoc>(), svalBuilder.getConditionType()); - + Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); if (!upperboundToCheck) break; - + ProgramStateRef state_exceedsUpperBound, state_withinUpperBound; std::tie(state_exceedsUpperBound, state_withinUpperBound) = state->assume(*upperboundToCheck); @@ -161,19 +161,19 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted); return; } - + // If we are constrained enough to definitely exceed the upper bound, report. if (state_exceedsUpperBound) { assert(!state_withinUpperBound); reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); return; } - + assert(state_withinUpperBound); state = state_withinUpperBound; } while (false); - + if (state != originalState) checkerContext.addTransition(state); } @@ -181,8 +181,8 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind) const { - - ExplodedNode *errorNode = checkerContext.generateSink(errorState); + + ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); if (!errorNode) return; @@ -259,7 +259,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, { const MemRegion *region = location.getAsRegion(); SVal offset = UndefinedVal(); - + while (region) { switch (region->getKind()) { default: { @@ -280,7 +280,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, ASTContext &astContext = svalBuilder.getContext(); if (elemType->isIncompleteType()) return RegionRawOffsetV2(); - + // Update the offset. offset = addValue(state, getValue(offset, svalBuilder), diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index f763284..26d42ba 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -140,10 +140,10 @@ void NilArgChecker::warnIfNilExpr(const Expr *E, ProgramStateRef State = C.getState(); if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { generateBugReport(N, Msg, E->getSourceRange(), E, C); } - + } } @@ -156,8 +156,8 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C, ProgramStateRef State = C.getState(); if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; - - if (ExplodedNode *N = C.generateSink()) { + + if (ExplodedNode *N = C.generateErrorNode()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -193,7 +193,7 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C, os << "' cannot be nil"; } } - + generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), msg.getArgExpr(Arg), C); } @@ -224,7 +224,7 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, static const unsigned InvalidArgIndex = UINT_MAX; unsigned Arg = InvalidArgIndex; bool CanBeSubscript = false; - + if (Class == FC_NSString) { Selector S = msg.getSelector(); @@ -307,8 +307,7 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, warnIfNilArg(C, msg, /* Arg */1, Class); } else if (S == SetObjectForKeyedSubscriptSel) { CanBeSubscript = true; - Arg = 0; - warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); + Arg = 1; } else if (S == RemoveObjectForKeySel) { Arg = 0; } @@ -433,7 +432,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; - + ASTContext &Ctx = C.getASTContext(); if (!II) II = &Ctx.Idents.get("CFNumberCreate"); @@ -489,23 +488,24 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, if (SourceSize == TargetSize) return; - // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; - // otherwise generate a regular node. + // Generate an error. Only generate a sink error node + // if 'SourceSize < TargetSize'; otherwise generate a non-fatal error 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.addTransition()) { + ExplodedNode *N = SourceSize < TargetSize ? C.generateErrorNode() + : C.generateNonFatalErrorNode(); + if (N) { 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." ; @@ -549,7 +549,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; - + if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); @@ -589,7 +589,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { - ExplodedNode *N = C.generateSink(stateTrue); + ExplodedNode *N = C.generateErrorNode(stateTrue); if (!N) return; @@ -635,7 +635,7 @@ public: void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { - + if (!BT) { BT.reset(new APIMisuse( this, "message incorrectly sent to class instead of class instance")); @@ -646,7 +646,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, autoreleaseS = GetNullarySelector("autorelease", Ctx); drainS = GetNullarySelector("drain", Ctx); } - + if (msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); @@ -655,8 +655,8 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, Selector S = msg.getSelector(); if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; - - if (ExplodedNode *N = C.addTransition()) { + + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { SmallString<200> buf; llvm::raw_svector_ostream os(buf); @@ -665,7 +665,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, os << "' message should be sent to instances " "of class '" << Class->getName() << "' and not the class directly"; - + auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); report->addRange(msg.getSourceRange()); C.emitReport(std::move(report)); @@ -699,12 +699,12 @@ public: bool VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { const ObjCMethodDecl *MD = msg.getDecl(); - + if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) return false; - + Selector S = msg.getSelector(); - + if (msg.isInstanceMessage()) { // FIXME: Ideally we'd look at the receiver interface here, but that's not // useful for init, because alloc returns 'id'. In theory, this could lead @@ -751,7 +751,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, ASTContext &Ctx = C.getASTContext(); arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); - dictionaryWithObjectsAndKeysS = + dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); @@ -789,18 +789,18 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, // Ignore pointer constants. if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) continue; - + // Ignore pointer types annotated with 'NSObject' attribute. if (C.getASTContext().isObjCNSObjectType(ArgTy)) continue; - + // Ignore CF references, which can be toll-free bridged. if (coreFoundation::isCFObjectRef(ArgTy)) continue; // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) - errorNode = C.addTransition(); + errorNode = C.generateNonFatalErrorNode(); if (!errorNode.getValue()) continue; @@ -861,7 +861,7 @@ static bool isKnownNonNilCollectionType(QualType T) { const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); if (!PT) return false; - + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); if (!ID) return false; @@ -992,9 +992,7 @@ static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, ProgramPoint P = N->getLocation(); if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { - if (BE->getSrc()->getLoopTarget() == FCS) - return true; - return false; + return BE->getSrc()->getLoopTarget() == FCS; } // Keep looking for a block edge. @@ -1023,9 +1021,9 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, State = checkElementNonNil(C, State, FCS); State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); } - + if (!State) - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); else if (State != C.getState()) C.addTransition(State); } @@ -1038,11 +1036,8 @@ bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, CountSelectorII = &C.getASTContext().Idents.get("count"); // If the method returns collection count, record the value. - if (S.isUnarySelector() && - (S.getIdentifierInfoForSlot(0) == CountSelectorII)) - return true; - - return false; + return S.isUnarySelector() && + (S.getIdentifierInfoForSlot(0) == CountSelectorII); } void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, @@ -1069,7 +1064,7 @@ void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, // a call to "count" and add it to the map. if (!isCollectionCountMethod(M, C)) return; - + const Expr *MsgExpr = M.getOriginExpr(); SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); if (CountS) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index e945c38..f26f731 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -32,7 +32,7 @@ namespace { void BoolAssignmentChecker::emitReport(ProgramStateRef state, CheckerContext &C) const { - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT) BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value")); C.emitReport(llvm::make_unique<BugReport>(*BT, BT->getDescription(), N)); @@ -42,45 +42,45 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef state, static bool isBooleanType(QualType Ty) { if (Ty->isBooleanType()) // C++ or C99 return true; - + if (const TypedefType *TT = Ty->getAs<TypedefType>()) return TT->getDecl()->getName() == "BOOL" || // Objective-C TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99 TT->getDecl()->getName() == "Boolean"; // MacTypes.h - + return false; } void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const { - + // We are only interested in stores into Booleans. const TypedValueRegion *TR = dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion()); - + if (!TR) return; - + QualType valTy = TR->getValueType(); - + if (!isBooleanType(valTy)) return; - + // Get the value of the right-hand side. We only care about values // that are defined (UnknownVals and UndefinedVals are handled by other // checkers). Optional<DefinedSVal> DV = val.getAs<DefinedSVal>(); if (!DV) return; - + // Check if the assigned value meets our criteria for correctness. It must // be a value that is either 0 or 1. One way to check this is to see if // the value is possibly < 0 (for a negative value) or greater than 1. - ProgramStateRef state = C.getState(); + ProgramStateRef state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); ConstraintManager &CM = C.getConstraintManager(); - - // First, ensure that the value is >= 0. + + // First, ensure that the value is >= 0. DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy); SVal greaterThanOrEqualToZeroVal = svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal, @@ -91,13 +91,13 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, if (!greaterThanEqualToZero) { // The SValBuilder cannot construct a valid SVal for this condition. - // This means we cannot properly reason about it. + // This means we cannot properly reason about it. return; } - + ProgramStateRef stateLT, stateGE; std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero); - + // Is it possible for the value to be less than zero? if (stateLT) { // It is possible for the value to be less than zero. We only @@ -106,15 +106,15 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, // value is underconstrained and there is nothing left to be done. if (!stateGE) emitReport(stateLT, C); - + // In either case, we are done. return; } - + // If we reach here, it must be the case that the value is constrained // to only be >= 0. assert(stateGE == state); - + // At this point we know that the value is >= 0. // Now check to ensure that the value is <= 1. DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy); @@ -127,13 +127,13 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, if (!lessThanEqToOne) { // The SValBuilder cannot construct a valid SVal for this condition. - // This means we cannot properly reason about it. + // This means we cannot properly reason about it. return; } - + ProgramStateRef stateGT, stateLE; std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne); - + // Is it possible for the value to be greater than one? if (stateGT) { // It is possible for the value to be greater than one. We only @@ -142,11 +142,11 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, // value is underconstrained and there is nothing left to be done. if (!stateLE) emitReport(stateGT, C); - + // In either case, we are done. return; } - + // If we reach here, it must be the case that the value is constrained // to only be <= 1. assert(stateLE == state); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 104a81e..dab2f61 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -41,11 +41,12 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, default: return false; + case Builtin::BI__builtin_unpredictable: case Builtin::BI__builtin_expect: case Builtin::BI__builtin_assume_aligned: case Builtin::BI__builtin_addressof: { - // For __builtin_expect and __builtin_assume_aligned, just return the value - // of the subexpression. + // For __builtin_unpredictable, __builtin_expect, and + // __builtin_assume_aligned, just return the value of the subexpression. // __builtin_addressof is going from a reference to a pointer, but those // are represented the same way in the analyzer. assert (CE->arg_begin() != CE->arg_end()); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 54b1241..5d78d9b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -65,7 +65,7 @@ public: void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; bool wantsRegionChangeUpdate(ProgramStateRef state) const; - ProgramStateRef + ProgramStateRef checkRegionChanges(ProgramStateRef state, const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, @@ -92,7 +92,7 @@ public: void evalstrLength(CheckerContext &C, const CallExpr *CE) const; void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; void evalstrLengthCommon(CheckerContext &C, - const CallExpr *CE, + const CallExpr *CE, bool IsStrnlen = false) const; void evalStrcpy(CheckerContext &C, const CallExpr *CE) const; @@ -137,15 +137,16 @@ public: SVal Buf, bool hypothetical = false) const; - const StringLiteral *getCStringLiteral(CheckerContext &C, + const StringLiteral *getCStringLiteral(CheckerContext &C, ProgramStateRef &state, - const Expr *expr, + const Expr *expr, SVal val) const; static ProgramStateRef InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *Ex, SVal V, - bool IsSourceBuffer); + bool IsSourceBuffer, + const Expr *Size); static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); @@ -193,6 +194,14 @@ public: ProgramStateRef state, NonLoc left, NonLoc right) const; + + // Return true if the destination buffer of the copy function may be in bound. + // Expects SVal of Size to be positive and unsigned. + // Expects SVal of FirstBuf to be a FieldRegion. + static bool IsFirstBufInBound(CheckerContext &C, + ProgramStateRef state, + const Expr *FirstBuf, + const Expr *Size); }; } //end anonymous namespace @@ -229,7 +238,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, if (!Filter.CheckCStringNullArg) return nullptr; - ExplodedNode *N = C.generateSink(stateNull); + ExplodedNode *N = C.generateErrorNode(stateNull); if (!N) return nullptr; @@ -282,7 +291,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, // Get the size of the array. const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); SValBuilder &svalBuilder = C.getSValBuilder(); - SVal Extent = + SVal Extent = svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>(); @@ -292,7 +301,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return nullptr; @@ -327,7 +336,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, C.emitReport(std::move(report)); return nullptr; } - + // Array bound check succeeded. From this point forward the array bound // should always succeed. return StInBound; @@ -442,7 +451,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, return state; // Are the two values the same? - SValBuilder &svalBuilder = C.getSValBuilder(); + SValBuilder &svalBuilder = C.getSValBuilder(); std::tie(stateTrue, stateFalse) = state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc)); @@ -489,7 +498,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, // Bail out if the cast fails. ASTContext &Ctx = svalBuilder.getContext(); QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); - SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, + SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First->getType()); Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); if (!FirstStartLoc) @@ -525,7 +534,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, const Stmt *Second) const { - ExplodedNode *N = C.generateSink(state); + ExplodedNode *N = C.generateErrorNode(state); if (!N) return; @@ -568,7 +577,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, } else { // Try switching the operands. (The order of these two assignments is // important!) - maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left, + maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left, sizeTy); left = right; } @@ -585,7 +594,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, if (stateOverflow && !stateOkay) { // We have an overflow. Emit a bug report. - ExplodedNode *N = C.generateSink(stateOverflow); + ExplodedNode *N = C.generateErrorNode(stateOverflow); if (!N) return nullptr; @@ -706,7 +715,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, if (!Filter.CheckCStringNotNullTerm) return UndefinedVal(); - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_NotCString) BT_NotCString.reset(new BuiltinBug( Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, @@ -723,7 +732,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N); report->addRange(Ex->getSourceRange()); - C.emitReport(std::move(report)); + C.emitReport(std::move(report)); } return UndefinedVal(); @@ -766,7 +775,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, if (!Filter.CheckCStringNotNullTerm) return UndefinedVal(); - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_NotCString) BT_NotCString.reset(new BuiltinBug( Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, @@ -787,7 +796,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N); report->addRange(Ex->getSourceRange()); - C.emitReport(std::move(report)); + C.emitReport(std::move(report)); } return UndefinedVal(); @@ -814,10 +823,74 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, return strRegion->getStringLiteral(); } +bool CStringChecker::IsFirstBufInBound(CheckerContext &C, + ProgramStateRef state, + const Expr *FirstBuf, + const Expr *Size) { + // If we do not know that the buffer is long enough we return 'true'. + // Otherwise the parent region of this field region would also get + // invalidated, which would lead to warnings based on an unknown state. + + // Originally copied from CheckBufferAccess and CheckLocation. + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + const LocationContext *LCtx = C.getLocationContext(); + + QualType sizeTy = Size->getType(); + QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal BufVal = state->getSVal(FirstBuf, LCtx); + + SVal LengthVal = state->getSVal(Size, LCtx); + Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + if (!Length) + return true; // cf top comment. + + // Compute the offset of the last element to be accessed: size-1. + NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); + NonLoc LastOffset = + svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy) + .castAs<NonLoc>(); + + // Check that the first buffer is sufficiently long. + SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); + Optional<Loc> BufLoc = BufStart.getAs<Loc>(); + if (!BufLoc) + return true; // cf top comment. + + SVal BufEnd = + svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy); + + // Check for out of bound array element access. + const MemRegion *R = BufEnd.getAsRegion(); + if (!R) + return true; // cf top comment. + + const ElementRegion *ER = dyn_cast<ElementRegion>(R); + if (!ER) + return true; // cf top comment. + + assert(ER->getValueType() == C.getASTContext().CharTy && + "IsFirstBufInBound should only be called with char* ElementRegions"); + + // Get the size of the array. + const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); + SVal Extent = + svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + DefinedOrUnknownSVal ExtentSize = Extent.castAs<DefinedOrUnknownSVal>(); + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + + ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true); + + return static_cast<bool>(StInBound); +} + ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *E, SVal V, - bool IsSourceBuffer) { + bool IsSourceBuffer, + const Expr *Size) { Optional<Loc> L = V.getAs<Loc>(); if (!L) return state; @@ -843,13 +916,23 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, // Invalidate and escape only indirect regions accessible through the source // buffer. if (IsSourceBuffer) { - ITraits.setTrait(R, + ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_PreserveContents); ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape); CausesPointerEscape = true; + } else { + const MemRegion::Kind& K = R->getKind(); + if (K == MemRegion::FieldRegionKind) + if (Size && IsFirstBufInBound(C, state, E, Size)) { + // If destination buffer is a field region and access is in bound, + // do not invalidate its super region. + ITraits.setTrait( + R, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + } } - return state->invalidateRegions(R, E, C.blockCount(), LCtx, + return state->invalidateRegions(R, E, C.blockCount(), LCtx, CausesPointerEscape, nullptr, nullptr, &ITraits); } @@ -901,7 +984,7 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, // evaluation of individual function calls. //===----------------------------------------------------------------------===// -void CStringChecker::evalCopyCommon(CheckerContext &C, +void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, const Expr *Size, const Expr *Dest, @@ -941,7 +1024,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // Get the value of the Src. SVal srcVal = state->getSVal(Source, LCtx); - + // Ensure the source is not null. If it is NULL there will be a // NULL pointer dereference. state = checkNonNull(C, state, Source, srcVal); @@ -959,11 +1042,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, if (!state) return; - // If this is mempcpy, get the byte after the last byte copied and + // If this is mempcpy, get the byte after the last byte copied and // bind the expr. if (IsMempcpy) { loc::MemRegionVal destRegVal = destVal.castAs<loc::MemRegionVal>(); - + // Get the length to copy. if (Optional<NonLoc> lenValNonLoc = sizeVal.getAs<NonLoc>()) { // Get the byte after the last byte copied. @@ -972,11 +1055,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); loc::MemRegionVal DestRegCharVal = SvalBuilder.evalCast(destRegVal, CharPtrTy, Dest->getType()).castAs<loc::MemRegionVal>(); - SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add, + SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add, DestRegCharVal, - *lenValNonLoc, + *lenValNonLoc, Dest->getType()); - + // The byte after the last byte copied is the return value. state = state->BindExpr(CE, LCtx, lastElement); } else { @@ -999,13 +1082,13 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // 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, C.getSVal(Dest), - /*IsSourceBuffer*/false); + state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest), + /*IsSourceBuffer*/false, Size); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). - state = InvalidateBuffer(C, state, Source, C.getSVal(Source), - /*IsSourceBuffer*/true); + state = InvalidateBuffer(C, state, Source, C.getSVal(Source), + /*IsSourceBuffer*/true, nullptr); C.addTransition(state); } @@ -1032,7 +1115,7 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { // The return value is a pointer to the byte following the last written byte. const Expr *Dest = CE->getArg(0); ProgramStateRef state = C.getState(); - + evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true); } @@ -1053,7 +1136,7 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { return; // void bcopy(const void *src, void *dst, size_t n); - evalCopyCommon(C, CE, C.getState(), + evalCopyCommon(C, CE, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0)); } @@ -1244,7 +1327,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, state, BO_LE, resultNL, *strLengthNL, cmpTy) .castAs<DefinedOrUnknownSVal>(), true); } - + if (maxlenValNL) { state = state->assume(C.getSValBuilder().evalBinOpNN( state, BO_LE, resultNL, *maxlenValNL, cmpTy) @@ -1275,8 +1358,8 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { return; // char *strcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, - /* returnEnd = */ false, + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, /* isBounded = */ false, /* isAppending = */ false); } @@ -1286,8 +1369,8 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { return; // char *strncpy(char *restrict dst, const char *restrict src, size_t n); - evalStrcpyCommon(C, CE, - /* returnEnd = */ false, + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, /* isBounded = */ true, /* isAppending = */ false); } @@ -1297,8 +1380,8 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { return; // char *stpcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, - /* returnEnd = */ true, + evalStrcpyCommon(C, CE, + /* returnEnd = */ true, /* isBounded = */ false, /* isAppending = */ false); } @@ -1308,8 +1391,8 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { return; //char *strcat(char *restrict s1, const char *restrict s2); - evalStrcpyCommon(C, CE, - /* returnEnd = */ false, + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, /* isBounded = */ false, /* isAppending = */ true); } @@ -1319,8 +1402,8 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { return; //char *strncat(char *restrict s1, const char *restrict s2, size_t n); - evalStrcpyCommon(C, CE, - /* returnEnd = */ false, + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, /* isBounded = */ true, /* isAppending = */ true); } @@ -1515,7 +1598,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>(); Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); - + // If we know both string lengths, we might know the final string length. if (srcStrLengthNL && dstStrLengthNL) { // Make sure the two lengths together don't overflow a size_t. @@ -1523,7 +1606,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (!state) return; - finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL, + finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL, *dstStrLengthNL, sizeTy); } @@ -1586,7 +1669,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); - state = CheckLocation(C, state, CE->getArg(2), maxLastElement, + state = CheckLocation(C, state, CE->getArg(2), maxLastElement, boundWarning); if (!state) return; @@ -1620,11 +1703,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // 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, - /*IsSourceBuffer*/false); + /*IsSourceBuffer*/false, nullptr); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). - state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true); + state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true, + nullptr); // Set the C string length of the destination, if we know it. if (isBounded && !isAppending) { @@ -1667,7 +1751,7 @@ void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false); } -void CStringChecker::evalStrcasecmp(CheckerContext &C, +void CStringChecker::evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const { if (CE->getNumArgs() < 2) return; @@ -1676,7 +1760,7 @@ void CStringChecker::evalStrcasecmp(CheckerContext &C, evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true); } -void CStringChecker::evalStrncasecmp(CheckerContext &C, +void CStringChecker::evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const { if (CE->getNumArgs() < 3) return; @@ -1848,7 +1932,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { // Invalidate the search string, representing the change of one delimiter // character to NUL. State = InvalidateBuffer(C, State, SearchStrPtr, Result, - /*IsSourceBuffer*/false); + /*IsSourceBuffer*/false, nullptr); // Overwrite the search string pointer. The new value is either an address // further along in the same string, or NULL if there are no more tokens. @@ -1915,7 +1999,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { evalFunction = &CStringChecker::evalBcopy; else if (C.isCLibraryFunction(FDecl, "bcmp")) evalFunction = &CStringChecker::evalMemcmp; - + // If the callee isn't a string function, let another checker handle it. if (!evalFunction) return false; @@ -1929,10 +2013,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // properties are held. However, if the user chooses to turn off some of these // checks, we ignore the issues and leave the call evaluation to a generic // handler. - if (!C.isDifferent()) - return false; - - return true; + return C.isDifferent(); } void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { @@ -1975,7 +2056,7 @@ bool CStringChecker::wantsRegionChangeUpdate(ProgramStateRef state) const { return !Entries.isEmpty(); } -ProgramStateRef +ProgramStateRef CStringChecker::checkRegionChanges(ProgramStateRef state, const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 26423b7..1459083 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -40,6 +40,7 @@ class CallAndMessageChecker : public Checker< check::PreStmt<CallExpr>, check::PreStmt<CXXDeleteExpr>, check::PreObjCMessage, + check::ObjCMessageNil, check::PreCall > { mutable std::unique_ptr<BugType> BT_call_null; mutable std::unique_ptr<BugType> BT_call_undef; @@ -60,6 +61,12 @@ public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + + /// Fill in the return value that results from messaging nil based on the + /// return type and architecture and diagnose if the return value will be + /// garbage. + void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: @@ -82,7 +89,7 @@ private: BT.reset(new BuiltinBug(this, desc)); } bool uninitRefOrPointer(CheckerContext &C, const SVal &V, - const SourceRange &ArgRange, + SourceRange ArgRange, const Expr *ArgEx, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD) const; }; @@ -90,7 +97,7 @@ private: void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -131,7 +138,7 @@ static StringRef describeUninitializedArgumentInCall(const CallEvent &Call, bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, const SVal &V, - const SourceRange &ArgRange, + SourceRange ArgRange, const Expr *ArgEx, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, @@ -162,7 +169,7 @@ bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, const ProgramStateRef State = C.getState(); const SVal PSV = State->getSVal(SValMemRegion); if (PSV.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); auto R = llvm::make_unique<BugReport>(*BT, Message, N); R->addRange(ArgRange); @@ -193,7 +200,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, return true; if (V.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); // Generate a report for this bug. @@ -258,7 +265,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, D->getStore()); if (F.Find(D->getRegion())) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); SmallString<512> Str; llvm::raw_svector_ostream os(Str); @@ -331,7 +338,7 @@ void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE, SVal Arg = C.getSVal(DE->getArgument()); if (Arg.isUndef()) { StringRef Desc; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT_cxx_delete_undef) @@ -388,7 +395,7 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, // the function. unsigned Params = FD->getNumParams(); if (Call.getNumArgs() < Params) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -436,7 +443,7 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { SVal recVal = msg.getReceiverSVal(); if (recVal.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { BugType *BT = nullptr; switch (msg.getMessageKind()) { case OCM_Message: @@ -471,22 +478,14 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, C.emitReport(std::move(R)); } return; - } else { - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = recVal.castAs<DefinedOrUnknownSVal>(); - - ProgramStateRef state = C.getState(); - ProgramStateRef notNilState, nilState; - std::tie(notNilState, nilState) = state->assume(receiverVal); - - // Handle receiver must be nil. - if (nilState && !notNilState) { - HandleNilReceiver(C, state, msg); - return; - } } } +void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg, + CheckerContext &C) const { + HandleNilReceiver(C, C.getState(), msg); +} + void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, ExplodedNode *N) const { @@ -523,7 +522,8 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, static bool supportsNilWithFloatRet(const llvm::Triple &triple) { return (triple.getVendor() == llvm::Triple::Apple && - (triple.isiOS() || !triple.isMacOSXVersionLT(10,5))); + (triple.isiOS() || triple.isWatchOS() || + !triple.isMacOSXVersionLT(10,5))); } void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, @@ -560,7 +560,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy)))) { - if (ExplodedNode *N = C.generateSink(state, nullptr, &Tag)) + if (ExplodedNode *N = C.generateErrorNode(state, &Tag)) emitNilReceiverBug(C, Msg, N); return; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 0d683f9..2337400 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -82,10 +82,7 @@ static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize, if (Left.isNegative()) return false; - if (Left % FlexSize == 0) - return true; - - return false; + return Left % FlexSize == 0; } void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { @@ -131,7 +128,7 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) return; - if (ExplodedNode *errorNode = C.generateSink()) { + if (ExplodedNode *errorNode = C.generateErrorNode()) { if (!BT) BT.reset(new BuiltinBug(this, "Cast region with wrong size.", "Cast a region whose size is not a multiple" diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index ba3024d..fa78413 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -56,7 +56,7 @@ void CastToStructChecker::checkPreStmt(const CastExpr *CE, // Now the cast-to-type is struct pointer, the original type is not void*. if (!OrigPointeeTy->isRecordType()) { - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Cast from non-struct type to struct type", diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 12eb0bd..25caa00 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -51,7 +51,7 @@ static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, if (E->getDecl()->getIdentifier() == SelfII) if (ME->getMethodDecl() == PD->getSetterMethodDecl() && ME->getNumArgs() == 1 && - ME->getArg(0)->isNullPointerConstant(Ctx, + ME->getArg(0)->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) return true; @@ -61,7 +61,7 @@ static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts())) if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) - if (BO->getRHS()->isNullPointerConstant(Ctx, + if (BO->getRHS()->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) { // This is only a 'release' if the property kind is not // 'assign'. diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index e0c113c..60f1618 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -86,8 +86,7 @@ public: // Helpers. bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); - typedef void (WalkAST::*FnCheck)(const CallExpr *, - const FunctionDecl *); + typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *); // Checker-specific methods. void checkLoopConditionForFloat(const ForStmt *FS); @@ -115,7 +114,7 @@ void WalkAST::VisitChildren(Stmt *S) { } void WalkAST::VisitCallExpr(CallExpr *CE) { - // Get the callee. + // Get the callee. const FunctionDecl *FD = CE->getDirectCallee(); if (!FD) @@ -307,7 +306,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { if (!filter.check_gets) return; - + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); if (!FPT) return; @@ -434,18 +433,18 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { .Case("mkdtemp", std::make_pair(0,-1)) .Case("mkstemps", std::make_pair(0,1)) .Default(std::make_pair(-1, -1)); - + assert(ArgSuffix.first >= 0 && "Unsupported function"); // Check if the number of arguments is consistent with out expectations. unsigned numArgs = CE->getNumArgs(); if ((signed) numArgs <= ArgSuffix.first) return; - + const StringLiteral *strArg = dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) ->IgnoreParenImpCasts()); - + // Currently we only handle string literals. It is possible to do better, // either by looking at references to const variables, or by doing real // flow analysis. @@ -470,13 +469,13 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { suffix = (unsigned) Result.getZExtValue(); n = (n > suffix) ? n - suffix : 0; } - + for (unsigned i = 0; i < n; ++i) if (str[i] == 'X') ++numX; - + if (numX >= 6) return; - + // Issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); @@ -502,13 +501,13 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// // Check: Any use of 'strcpy' is insecure. // -// CWE-119: Improper Restriction of Operations within -// the Bounds of a Memory Buffer +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer //===----------------------------------------------------------------------===// void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { if (!filter.check_strcpy) return; - + if (!checkCall_strCommon(CE, FD)) return; @@ -529,8 +528,8 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// // Check: Any use of 'strcat' is insecure. // -// CWE-119: Improper Restriction of Operations within -// the Bounds of a Memory Buffer +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer //===----------------------------------------------------------------------===// void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { if (!filter.check_strcpy) @@ -684,7 +683,7 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { if (!filter.check_UncheckedReturn) return; - + const FunctionDecl *FD = CE->getDirectCallee(); if (!FD) return; @@ -749,7 +748,7 @@ namespace { class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { public: ChecksFilter filter; - + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index 81a2006..e079a8c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -55,8 +55,8 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { 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 + // 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())) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 956dca7..37b8448 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -38,6 +38,7 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>, check::PostStmt<DeclStmt>, check::PreObjCMessage, check::PostObjCMessage, + check::ObjCMessageNil, check::PreCall, check::PostCall, check::BranchCondition, @@ -95,6 +96,15 @@ public: /// check::PostObjCMessage void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} + /// \brief Visit an Objective-C message whose receiver is nil. + /// + /// This will be called when the analyzer core processes a method call whose + /// receiver is definitely nil. In this case, check{Pre/Post}ObjCMessage and + /// check{Pre/Post}Call will not be called. + /// + /// check::ObjCMessageNil + void checkObjCMessageNil(const ObjCMethodCall &M, CheckerContext &C) const {} + /// \brief Pre-visit an abstract "call" event. /// /// This is used for checkers that want to check arguments or attributed @@ -222,7 +232,7 @@ public: /// changed, this allows the analyzer core to skip the more expensive /// #checkRegionChanges when no checkers are tracking any state. bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; } - + /// \brief Called when the contents of one or more regions change. /// /// This can occur in many different ways: an explicit bind, a blanket @@ -246,7 +256,7 @@ public: /// #wantsRegionChangeUpdate returns \c true. /// /// check::RegionChanges - ProgramStateRef + ProgramStateRef checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, @@ -259,12 +269,12 @@ public: /// /// This notifies the checkers about pointer escape, which occurs whenever /// the analyzer cannot track the symbol any more. For example, as a - /// result of assigning a pointer into a global or when it's passed to a + /// result of assigning a pointer into a global or when it's passed to a /// function call the analyzer cannot model. - /// + /// /// \param State The state at the point of escape. /// \param Escaped The list of escaped symbols. - /// \param Call The corresponding CallEvent, if the symbols escape as + /// \param Call The corresponding CallEvent, if the symbols escape as /// parameters to the given call. /// \param Kind How the symbols have escaped. /// \returns Checkers can modify the state by returning a new state. @@ -285,7 +295,7 @@ public: PointerEscapeKind Kind) const { return State; } - + /// check::Event<ImplicitNullDerefEvent> void checkEvent(ImplicitNullDerefEvent Event) const {} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td index d1d6ac2..8133d29 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td @@ -13,6 +13,11 @@ include "clang/StaticAnalyzer/Checkers/CheckerBase.td" // Packages. //===----------------------------------------------------------------------===// +// The Alpha package is for checkers that have too many false positives to be +// turned on by default. The hierarchy under Alpha should be organized in the +// hierarchy checkers would have had if they were truly at the top level. +// (For example, a Cocoa-specific checker that is alpha should be in +// alpha.osx.cocoa). def Alpha : Package<"alpha">; def Core : Package<"core">; @@ -20,16 +25,33 @@ def CoreBuiltin : Package<"builtin">, InPackage<Core>; def CoreUninitialized : Package<"uninitialized">, InPackage<Core>; def CoreAlpha : Package<"core">, InPackage<Alpha>, Hidden; +// The OptIn package is for checkers that are not alpha and that would normally +// be on by default but where the driver does not have enough information to +// determine when they are applicable. For example, localizability checkers fit +// this criterion because the driver cannot determine whether a project is +// localized or not -- this is best determined at the IDE or build-system level. +// +// The checker hierarchy under OptIn should mirror that in Alpha: checkers +// should be organized as if they were at the top level. +// +// Note: OptIn is *not* intended for checkers that are too noisy to be on by +// default. Such checkers belong in the alpha package. +def OptIn : Package<"optin">; + +def Nullability : Package<"nullability">; + def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, InPackage<Alpha>, Hidden; def DeadCode : Package<"deadcode">; def DeadCodeAlpha : Package<"deadcode">, InPackage<Alpha>, Hidden; +def Performance : Package<"performance">, InPackage<OptIn>; + def Security : Package <"security">; def InsecureAPI : Package<"insecureAPI">, InPackage<Security>; def SecurityAlpha : Package<"security">, InPackage<Alpha>, Hidden; -def Taint : Package<"taint">, InPackage<SecurityAlpha>, Hidden; +def Taint : Package<"taint">, InPackage<SecurityAlpha>, Hidden; def Unix : Package<"unix">; def UnixAlpha : Package<"unix">, InPackage<Alpha>, Hidden; @@ -38,11 +60,18 @@ def CStringAlpha : Package<"cstring">, InPackage<UnixAlpha>, Hidden; def OSX : Package<"osx">; def OSXAlpha : Package<"osx">, InPackage<Alpha>, Hidden; +def OSXOptIn : Package<"osx">, InPackage<OptIn>; + def Cocoa : Package<"cocoa">, InPackage<OSX>; def CocoaAlpha : Package<"cocoa">, InPackage<OSXAlpha>, Hidden; +def CocoaOptIn : Package<"cocoa">, InPackage<OSXOptIn>; + def CoreFoundation : Package<"coreFoundation">, InPackage<OSX>; def Containers : Package<"containers">, InPackage<CoreFoundation>; +def LocalizabilityAlpha : Package<"localizability">, InPackage<CocoaAlpha>; +def LocalizabilityOptIn : Package<"localizability">, InPackage<CocoaOptIn>; + def LLVM : Package<"llvm">; def Debug : Package<"debug">; @@ -128,8 +157,36 @@ def TestAfterDivZeroChecker : Checker<"TestAfterDivZero">, HelpText<"Check for division by variable that is later compared against 0. Either the comparison is useless or there is division by zero.">, DescFile<"TestAfterDivZeroChecker.cpp">; +def DynamicTypeChecker : Checker<"DynamicTypeChecker">, + HelpText<"Check for cases where the dynamic and the static type of an object are unrelated.">, + DescFile<"DynamicTypeChecker.cpp">; + } // end "alpha.core" +let ParentPackage = Nullability in { + +def NullPassedToNonnullChecker : Checker<"NullPassedToNonnull">, + HelpText<"Warns when a null pointer is passed to a pointer which has a _Nonnull type.">, + DescFile<"NullabilityChecker.cpp">; + +def NullReturnedFromNonnullChecker : Checker<"NullReturnedFromNonnull">, + HelpText<"Warns when a null pointer is returned from a function that has _Nonnull return type.">, + DescFile<"NullabilityChecker.cpp">; + +def NullableDereferencedChecker : Checker<"NullableDereferenced">, + HelpText<"Warns when a nullable pointer is dereferenced.">, + DescFile<"NullabilityChecker.cpp">; + +def NullablePassedToNonnullChecker : Checker<"NullablePassedToNonnull">, + HelpText<"Warns when a nullable pointer is passed to a pointer which has a _Nonnull type.">, + DescFile<"NullabilityChecker.cpp">; + +def NullableReturnedFromNonnullChecker : Checker<"NullablePassedToNonnull">, + HelpText<"Warns when a nullable pointer is returned from a function that has _Nonnull return type.">, + DescFile<"NullabilityChecker.cpp">; + +} // end "nullability" + //===----------------------------------------------------------------------===// // Evaluate "builtin" functions. //===----------------------------------------------------------------------===// @@ -167,7 +224,7 @@ def UndefBranchChecker : Checker<"Branch">, def UndefCapturedBlockVarChecker : Checker<"CapturedBlockVariable">, HelpText<"Check for blocks that capture uninitialized values">, DescFile<"UndefCapturedBlockVarChecker.cpp">; - + def ReturnUndefChecker : Checker<"UndefReturn">, HelpText<"Check for uninitialized values being returned to the caller">, DescFile<"ReturnUndefChecker.cpp">; @@ -181,11 +238,11 @@ def ReturnUndefChecker : Checker<"UndefReturn">, let ParentPackage = Cplusplus in { def NewDeleteChecker : Checker<"NewDelete">, - HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, + HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, - HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; } // end: "cplusplus" @@ -193,7 +250,7 @@ def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, let ParentPackage = CplusplusAlpha in { def VirtualCallChecker : Checker<"VirtualCall">, - HelpText<"Check virtual function calls during construction or destruction">, + HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; } // end: "alpha.cplusplus" @@ -218,6 +275,18 @@ def UnreachableCodeChecker : Checker<"UnreachableCode">, } // end "alpha.deadcode" //===----------------------------------------------------------------------===// +// Performance checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = Performance in { + +def PaddingChecker : Checker<"Padding">, + HelpText<"Check for excessively padded structs.">, + DescFile<"PaddingChecker.cpp">; + +} // end: "padding" + +//===----------------------------------------------------------------------===// // Security checkers. //===----------------------------------------------------------------------===// @@ -257,7 +326,7 @@ let ParentPackage = SecurityAlpha in { def ArrayBoundChecker : Checker<"ArrayBound">, HelpText<"Warn about buffer overflows (older checker)">, - DescFile<"ArrayBoundChecker.cpp">; + DescFile<"ArrayBoundChecker.cpp">; def ArrayBoundCheckerV2 : Checker<"ArrayBoundV2">, HelpText<"Warn about buffer overflows (newer checker)">, @@ -298,7 +367,7 @@ def UnixAPIChecker : Checker<"API">, def MallocChecker: Checker<"Malloc">, HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">, DescFile<"MallocChecker.cpp">; - + def MallocSizeofChecker : Checker<"MallocSizeof">, HelpText<"Check for dubious malloc arguments involving sizeof">, DescFile<"MallocSizeofChecker.cpp">; @@ -306,7 +375,11 @@ def MallocSizeofChecker : Checker<"MallocSizeof">, def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, HelpText<"Check for mismatched deallocators.">, DescFile<"MallocChecker.cpp">; - + +def VforkChecker : Checker<"Vfork">, + HelpText<"Check for proper usage of vfork">, + DescFile<"VforkChecker.cpp">; + } // end "unix" let ParentPackage = UnixAlpha in { @@ -337,7 +410,7 @@ def CStringNullArg : Checker<"NullArg">, def CStringSyntaxChecker : Checker<"BadSizeArg">, HelpText<"Check the size argument passed into C string functions for common erroneous patterns">, - DescFile<"CStringSyntaxChecker.cpp">; + DescFile<"CStringSyntaxChecker.cpp">; } let ParentPackage = CStringAlpha in { @@ -428,6 +501,10 @@ def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, DescFile<"RetainCountChecker.cpp">; +def ObjCGenericsChecker : Checker<"ObjCGenerics">, + HelpText<"Check for type errors when using Objective-C generics">, + DescFile<"DynamicTypePropagation.cpp">; + } // end "osx.cocoa" let ParentPackage = CocoaAlpha in { @@ -477,8 +554,25 @@ def ObjCContainersASTChecker : Checker<"PointerSizedValues">, def ObjCContainersChecker : Checker<"OutOfBounds">, HelpText<"Checks for index out-of-bounds when using 'CFArray' API">, DescFile<"ObjCContainersChecker.cpp">; - + } + +let ParentPackage = LocalizabilityOptIn in { +def NonLocalizedStringChecker : Checker<"NonLocalizedStringChecker">, + HelpText<"Warns about uses of non-localized NSStrings passed to UI methods expecting localized NSStrings">, + DescFile<"LocalizationChecker.cpp">; + +def EmptyLocalizationContextChecker : Checker<"EmptyLocalizationContextChecker">, + HelpText<"Check that NSLocalizedString macros include a comment for context">, + DescFile<"LocalizationChecker.cpp">; +} + +let ParentPackage = LocalizabilityAlpha in { +def PluralMisuseChecker : Checker<"PluralMisuseChecker">, + HelpText<"Warns against using one vs. many plural pattern in code when generating localized strings.">, + DescFile<"LocalizationChecker.cpp">; +} + //===----------------------------------------------------------------------===// // Checkers for LLVM development. //===----------------------------------------------------------------------===// @@ -546,4 +640,8 @@ def ExplodedGraphViewer : Checker<"ViewExplodedGraph">, HelpText<"View Exploded Graphs using GraphViz">, DescFile<"DebugCheckers.cpp">; +def BugHashDumper : Checker<"DumpBugHash">, + HelpText<"Dump the bug hash for all statements.">, + DescFile<"DebugCheckers.cpp">; + } // end "debug" diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 804e83c..3ad1996 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -27,7 +27,7 @@ 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; } @@ -50,7 +50,7 @@ public: static int x; return &x; } - + bool evalCall(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; @@ -87,8 +87,8 @@ bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); - - // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in + + // 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); @@ -106,7 +106,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. const Expr *ArgExpr = CE->getArg(0); SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext()); - + if (const MemRegion *R = ArgVal.getAsRegion()) { R = R->StripCasts(); if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) { @@ -135,12 +135,12 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // 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 = C.getState()->FindGDM(ChrootChecker::getTag()); if (k) if (isRootChanged((intptr_t) *k)) - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT_BreakJail) BT_BreakJail.reset(new BuiltinBug( this, "Break out of jail", "No call of chdir(\"/\") immediately " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index f4be5b3..f2a269a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -28,36 +28,36 @@ using namespace clang; using namespace ento; -namespace { - +namespace { + /// A simple visitor to record what VarDecls occur in EH-handling code. class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> { public: bool inEH; llvm::DenseSet<const VarDecl *> &S; - + bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { SaveAndRestore<bool> inFinally(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S); } - + bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) { SaveAndRestore<bool> inCatch(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S); } - + bool TraverseCXXCatchStmt(CXXCatchStmt *S) { SaveAndRestore<bool> inCatch(inEH, true); return TraverseStmt(S->getHandlerBlock()); } - + bool VisitDeclRefExpr(DeclRefExpr *DR) { if (inEH) if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl())) S.insert(D); return true; } - + EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) : inEH(false), S(S) {} }; @@ -70,9 +70,9 @@ class ReachableCode { public: ReachableCode(const CFG &cfg) : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {} - + void computeReachableBlocks(); - + bool isReachable(const CFGBlock *block) const { return reachable[block->getBlockID()]; } @@ -82,7 +82,7 @@ public: void ReachableCode::computeReachableBlocks() { if (!cfg.getNumBlockIDs()) return; - + SmallVector<const CFGBlock*, 10> worklist; worklist.push_back(&cfg.getEntry()); @@ -160,19 +160,19 @@ public: // to analyze that yet. return InEH->count(D); } - + void Report(const VarDecl *V, DeadStoreKind dsk, PathDiagnosticLocation 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; @@ -196,7 +196,7 @@ public: 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 + // where 'x' is unused later. We have yet to see a case where // this is a real bug. return; } @@ -259,7 +259,7 @@ public: const LiveVariables::LivenessValues &Live) override { currentBlock = block; - + // Skip statements in macros. if (S->getLocStart().isMacroID()) return; @@ -276,7 +276,7 @@ public: const Expr *RHS = LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); RHS = RHS->IgnoreParenCasts(); - + QualType T = VD->getType(); if (T->isPointerType() || T->isObjCObjectPointerType()) { if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) @@ -318,27 +318,27 @@ public: if (!V) continue; - - if (V->hasLocalStorage()) { + + if (V->hasLocalStorage()) { // Reference types confuse the dead stores checker. Skip them // for now. if (V->getType()->getAs<ReferenceType>()) return; - + if (const Expr *E = V->getInit()) { while (const ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E)) E = exprClean->getSubExpr(); - + // Look through transitive assignments, e.g.: // int x = y = 0; E = LookThroughTransitiveAssignmentsAndCommaOperators(E); - + // 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; - + // A dead initialization is a variable that is dead after it // is initialized. We don't flag warnings for those variables // marked 'unused' or 'objc_precise_lifetime'. @@ -401,6 +401,11 @@ public: // Check for '&'. Any VarDecl whose address has been taken we treat as // escaped. // FIXME: What about references? + if (auto *LE = dyn_cast<LambdaExpr>(S)) { + findLambdaReferenceCaptures(LE); + return; + } + const UnaryOperator *U = dyn_cast<UnaryOperator>(S); if (!U) return; @@ -412,6 +417,28 @@ public: if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) Escaped.insert(VD); } + + // Treat local variables captured by reference in C++ lambdas as escaped. + void findLambdaReferenceCaptures(const LambdaExpr *LE) { + const CXXRecordDecl *LambdaClass = LE->getLambdaClass(); + llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields; + FieldDecl *ThisCaptureField; + LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField); + + for (const LambdaCapture &C : LE->captures()) { + if (!C.capturesVariable()) + continue; + + VarDecl *VD = C.getCapturedVar(); + const FieldDecl *FD = CaptureFields[VD]; + if (!FD) + continue; + + // If the capture field is a reference type, it is capture-by-reference. + if (FD->getType()->isReferenceType()) + Escaped.insert(VD); + } + } }; } // end anonymous namespace diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 51e7a3d..2eef168 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -16,7 +16,10 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CallGraph.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/Support/Process.h" @@ -209,3 +212,36 @@ public: void ento::registerExplodedGraphViewer(CheckerManager &mgr) { mgr.registerChecker<ExplodedGraphViewer>(); } + +//===----------------------------------------------------------------------===// +// DumpBugHash +//===----------------------------------------------------------------------===// + +namespace { +class BugHashDumper : public Checker<check::PostStmt<Stmt>> { +public: + mutable std::unique_ptr<BugType> BT; + + void checkPostStmt(const Stmt *S, CheckerContext &C) const { + if (!BT) + BT.reset(new BugType(this, "Dump hash components", "debug")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + const LangOptions &Opts = C.getLangOpts(); + const SourceManager &SM = C.getSourceManager(); + FullSourceLoc FL(S->getLocStart(), SM); + std::string HashContent = + GetIssueString(SM, FL, getCheckName().getName(), BT->getCategory(), + C.getLocationContext()->getDecl(), Opts); + + C.emitReport(llvm::make_unique<BugReport>(*BT, HashContent, N)); + } +}; +} + +void ento::registerBugHashDumper(CheckerManager &mgr) { + mgr.registerChecker<BugHashDumper>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 2ba7ea4..5dd2832 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -14,10 +14,12 @@ #include "ClangSACheckers.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprOpenMP.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" @@ -83,14 +85,14 @@ DereferenceChecker::AddDerefSource(raw_ostream &os, SourceLocation L = IV->getLocation(); Ranges.push_back(SourceRange(L, L)); break; - } + } } } void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, bool IsBind) const { // Generate an error node. - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); if (!N) return; @@ -110,15 +112,11 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, S = expr->IgnoreParenLValueCasts(); if (IsBind) { - if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) { - if (BO->isAssignmentOp()) - S = BO->getRHS(); - } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { - assert(DS->isSingleDecl() && "We process decls one by one"); - if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) - if (const Expr *Init = VD->getAnyInitializer()) - S = Init; - } + const VarDecl *VD; + const Expr *Init; + std::tie(VD, Init) = parseAssignment(S); + if (VD && Init) + S = Init; } switch (S->getStmtClass()) { @@ -130,6 +128,14 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, os << " results in a null pointer dereference"; break; } + case Stmt::OMPArraySectionExprClass: { + os << "Array access"; + const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); + AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), + State.get(), N->getLocationContext()); + os << " results in a null pointer dereference"; + break; + } case Stmt::UnaryOperatorClass: { os << "Dereference of null pointer"; const UnaryOperator *U = cast<UnaryOperator>(S); @@ -159,7 +165,6 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, break; } - os.flush(); auto report = llvm::make_unique<BugReport>( *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); @@ -176,7 +181,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_undef) BT_undef.reset( new BuiltinBug(this, "Dereference of undefined pointer value")); @@ -211,8 +216,9 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, // 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)) { - ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; + if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { + ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), + /*IsDirectDereference=*/false}; dispatchEvent(event); } } @@ -248,9 +254,10 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, // At this point the value could be either null or non-null. // Record this as an "implicit" null dereference. - if (ExplodedNode *N = C.generateSink(StNull)) { - ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, - &C.getBugReporter() }; + if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { + ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, + &C.getBugReporter(), + /*IsDirectDereference=*/false}; dispatchEvent(event); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp index a71def2..ad478cb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -41,13 +41,12 @@ namespace { /// Checks for the init, dealloc, and any other functions that might be allowed /// to perform direct instance variable assignment based on their name. static bool DefaultMethodFilter(const ObjCMethodDecl *M) { - if (M->getMethodFamily() == OMF_init || M->getMethodFamily() == OMF_dealloc || - M->getMethodFamily() == OMF_copy || - M->getMethodFamily() == OMF_mutableCopy || - M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || - M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) - return true; - return false; + return M->getMethodFamily() == OMF_init || + M->getMethodFamily() == OMF_dealloc || + M->getMethodFamily() == OMF_copy || + M->getMethodFamily() == OMF_mutableCopy || + M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || + M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos; } class DirectIvarAssignment : diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 79f9479b..5985023 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -29,13 +29,13 @@ class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > { CheckerContext &C) const ; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; -}; +}; } // end anonymous namespace void DivZeroChecker::reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C) const { - if (ExplodedNode *N = C.generateSink(StateZero)) { + if (ExplodedNode *N = C.generateErrorNode(StateZero)) { if (!BT) BT.reset(new BuiltinBug(this, "Division by zero")); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp new file mode 100644 index 0000000..7e0cb8e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp @@ -0,0 +1,213 @@ +//== DynamicTypeChecker.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 looks for cases where the dynamic type of an object is unrelated +// to its static type. The type information utilized by this check is collected +// by the DynamicTypePropagation checker. This check does not report any type +// error for ObjC Generic types, in order to avoid duplicate erros from the +// ObjC Generics checker. This checker is not supposed to modify the program +// state, it is just the observer of the type information provided by other +// checkers. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> { + mutable std::unique_ptr<BugType> BT; + void initBugType() const { + if (!BT) + BT.reset( + new BugType(this, "Dynamic and static type mismatch", "Type Error")); + } + + class DynamicTypeBugVisitor + : public BugReporterVisitorImpl<DynamicTypeBugVisitor> { + public: + DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Reg); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked region. + const MemRegion *Reg; + }; + + void reportTypeError(QualType DynamicType, QualType StaticType, + const MemRegion *Reg, const Stmt *ReportedNode, + CheckerContext &C) const; + +public: + void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const; +}; +} + +void DynamicTypeChecker::reportTypeError(QualType DynamicType, + QualType StaticType, + const MemRegion *Reg, + const Stmt *ReportedNode, + CheckerContext &C) const { + initBugType(); + SmallString<192> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Object has a dynamic type '"; + QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), + llvm::Twine()); + OS << "' which is incompatible with static type '"; + QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), + llvm::Twine()); + OS << "'"; + std::unique_ptr<BugReport> R( + new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode())); + R->markInteresting(Reg); + R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg)); + R->addRange(ReportedNode->getSourceRange()); + C.emitReport(std::move(R)); +} + +PathDiagnosticPiece *DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode( + const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, + BugReport &BR) { + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = PrevN->getState(); + + DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg); + DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg); + if (!TrackedType.isValid()) + return nullptr; + + if (TrackedTypePrev.isValid() && + TrackedTypePrev.getType() == TrackedType.getType()) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = nullptr; + ProgramPoint ProgLoc = N->getLocation(); + if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) { + S = SP->getStmt(); + } + + if (!S) + return nullptr; + + const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Type '"; + QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "' is inferred from "; + + if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { + OS << "explicit cast (from '"; + QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { + OS << "implicit cast (from '"; + QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else { + OS << "this context"; + } + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(Pos, OS.str(), true, nullptr); +} + +static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) { + const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl(); + if (!Decl) + return false; + + return Decl->getDefinition(); +} + +// TODO: consider checking explicit casts? +void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE, + CheckerContext &C) const { + // TODO: C++ support. + if (CE->getCastKind() != CK_BitCast) + return; + + const MemRegion *Region = C.getSVal(CE).getAsRegion(); + if (!Region) + return; + + ProgramStateRef State = C.getState(); + DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region); + + if (!DynTypeInfo.isValid()) + return; + + QualType DynType = DynTypeInfo.getType(); + QualType StaticType = CE->getType(); + + const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>(); + const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>(); + + if (!DynObjCType || !StaticObjCType) + return; + + if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType)) + return; + + ASTContext &ASTCtxt = C.getASTContext(); + + // Strip kindeofness to correctly detect subtyping relationships. + DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); + StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); + + // Specialized objects are handled by the generics checker. + if (StaticObjCType->isSpecialized()) + return; + + if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType)) + return; + + if (DynTypeInfo.canBeASubClass() && + ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType)) + return; + + reportTypeError(DynType, StaticType, Region, CE, C); +} + +void ento::registerDynamicTypeChecker(CheckerManager &mgr) { + mgr.registerChecker<DynamicTypeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 43a2812..30f6298 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -7,42 +7,139 @@ // //===----------------------------------------------------------------------===// // +// This file contains two checkers. One helps the static analyzer core to track +// types, the other does type inference on Obj-C generics and report type +// errors. +// +// Dynamic Type Propagation: // This checker defines the rules for dynamic type gathering and propagation. // +// Generics Checker for Objective-C: +// This checker tries to find type errors that the compiler is not able to catch +// due to the implicit conversions that were introduced for backward +// compatibility. +// //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" using namespace clang; using namespace ento; +// ProgramState trait - The type inflation is tracked by DynamicTypeMap. This is +// an auxiliary map that tracks more information about generic types, because in +// some cases the most derived type is not the most informative one about the +// type parameters. This types that are stored for each symbol in this map must +// be specialized. +// TODO: In some case the type stored in this map is exactly the same that is +// stored in DynamicTypeMap. We should no store duplicated information in those +// cases. +REGISTER_MAP_WITH_PROGRAMSTATE(MostSpecializedTypeArgsMap, SymbolRef, + const ObjCObjectPointerType *) + namespace { class DynamicTypePropagation: public Checker< check::PreCall, check::PostCall, - check::PostStmt<ImplicitCastExpr>, - check::PostStmt<CXXNewExpr> > { + check::DeadSymbols, + check::PostStmt<CastExpr>, + check::PostStmt<CXXNewExpr>, + check::PreObjCMessage, + check::PostObjCMessage > { const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const; /// \brief Return a better dynamic type if one can be derived from the cast. const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, CheckerContext &C) const; + + ExplodedNode *dynamicTypePropagationOnCasts(const CastExpr *CE, + ProgramStateRef &State, + CheckerContext &C) const; + + mutable std::unique_ptr<BugType> ObjCGenericsBugType; + void initBugType() const { + if (!ObjCGenericsBugType) + ObjCGenericsBugType.reset( + new BugType(this, "Generics", categories::CoreFoundationObjectiveC)); + } + + class GenericsBugVisitor : public BugReporterVisitorImpl<GenericsBugVisitor> { + public: + GenericsBugVisitor(SymbolRef S) : Sym(S) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Sym); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked symbol. + SymbolRef Sym; + }; + + void reportGenericsBug(const ObjCObjectPointerType *From, + const ObjCObjectPointerType *To, ExplodedNode *N, + SymbolRef Sym, CheckerContext &C, + const Stmt *ReportedNode = nullptr) const; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; + void checkPostStmt(const CastExpr *CastE, CheckerContext &C) const; void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + /// This value is set to true, when the Generics checker is turned on. + DefaultBool CheckGenerics; }; } +void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + DynamicTypeMapImpl TypeMap = State->get<DynamicTypeMap>(); + for (DynamicTypeMapImpl::iterator I = TypeMap.begin(), E = TypeMap.end(); + I != E; ++I) { + if (!SR.isLiveRegion(I->first)) { + State = State->remove<DynamicTypeMap>(I->first); + } + } + + if (!SR.hasDeadSymbols()) { + C.addTransition(State); + return; + } + + MostSpecializedTypeArgsMapTy TyArgMap = + State->get<MostSpecializedTypeArgsMap>(); + for (MostSpecializedTypeArgsMapTy::iterator I = TyArgMap.begin(), + E = TyArgMap.end(); + I != E; ++I) { + if (SR.isDead(I->first)) { + State = State->remove<MostSpecializedTypeArgsMap>(I->first); + } + } + + C.addTransition(State); +} + static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, CheckerContext &C) { assert(Region); @@ -52,7 +149,7 @@ static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); ProgramStateRef State = C.getState(); - State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false); + State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubclass=*/false); C.addTransition(State); return; } @@ -113,7 +210,7 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, ProgramStateRef State = C.getState(); const ObjCMethodDecl *D = Msg->getDecl(); - + if (D && D->hasRelatedResultType()) { switch (Msg->getMethodFamily()) { default: @@ -131,7 +228,7 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, return; QualType DynResTy = C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); - C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); + C.addTransition(setDynamicTypeInfo(State, RetReg, DynResTy, false)); break; } case OMF_init: { @@ -140,8 +237,8 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); if (!RecReg) return; - DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); - C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); + DynamicTypeInfo RecDynType = getDynamicTypeInfo(State, RecReg); + C.addTransition(setDynamicTypeInfo(State, RetReg, RecDynType)); break; } } @@ -173,23 +270,25 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, } } -void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, - CheckerContext &C) const { - // We only track dynamic type info for regions. - const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); +/// TODO: Handle explicit casts. +/// Handle C++ casts. +/// +/// Precondition: the cast is between ObjCObjectPointers. +ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( + const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const { + // We only track type info for regions. + const MemRegion *ToR = C.getSVal(CE).getAsRegion(); if (!ToR) - return; + return C.getPredecessor(); + + if (isa<ExplicitCastExpr>(CE)) + return C.getPredecessor(); - switch (CastE->getCastKind()) { - default: - break; - case CK_BitCast: - // Only handle ObjCObjects for now. - if (const Type *NewTy = getBetterObjCType(CastE, C)) - C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0))); - break; + if (const Type *NewTy = getBetterObjCType(CE, C)) { + State = setDynamicTypeInfo(State, ToR, QualType(NewTy, 0)); + return C.addTransition(State); } - return; + return C.getPredecessor(); } void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, @@ -201,9 +300,9 @@ void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, const MemRegion *MR = C.getSVal(NewE).getAsRegion(); if (!MR) return; - - C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(), - /*CanBeSubclass=*/false)); + + C.addTransition(setDynamicTypeInfo(C.getState(), MR, NewE->getType(), + /*CanBeSubclass=*/false)); } const ObjCObjectType * @@ -254,7 +353,7 @@ DynamicTypePropagation::getBetterObjCType(const Expr *CastE, CastE->getType()->getAs<ObjCObjectPointerType>(); if (!NewTy) return nullptr; - QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType(); + QualType OldDTy = getDynamicTypeInfo(C.getState(), ToR).getType(); if (OldDTy.isNull()) { return NewTy; } @@ -276,6 +375,566 @@ DynamicTypePropagation::getBetterObjCType(const Expr *CastE, return nullptr; } +static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl( + const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, + const ObjCObjectPointerType *MostInformativeCandidate, ASTContext &C) { + // Checking if from and to are the same classes modulo specialization. + if (From->getInterfaceDecl()->getCanonicalDecl() == + To->getInterfaceDecl()->getCanonicalDecl()) { + if (To->isSpecialized()) { + assert(MostInformativeCandidate->isSpecialized()); + return MostInformativeCandidate; + } + return From; + } + const auto *SuperOfTo = + To->getObjectType()->getSuperClassType()->getAs<ObjCObjectType>(); + assert(SuperOfTo); + QualType SuperPtrOfToQual = + C.getObjCObjectPointerType(QualType(SuperOfTo, 0)); + const auto *SuperPtrOfTo = SuperPtrOfToQual->getAs<ObjCObjectPointerType>(); + if (To->isUnspecialized()) + return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, SuperPtrOfTo, + C); + else + return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, + MostInformativeCandidate, C); +} + +/// A downcast may loose specialization information. E. g.: +/// MutableMap<T, U> : Map +/// The downcast to MutableMap looses the information about the types of the +/// Map (due to the type parameters are not being forwarded to Map), and in +/// general there is no way to recover that information from the +/// declaration. In order to have to most information, lets find the most +/// derived type that has all the type parameters forwarded. +/// +/// Get the a subclass of \p From (which has a lower bound \p To) that do not +/// loose information about type parameters. \p To has to be a subclass of +/// \p From. From has to be specialized. +static const ObjCObjectPointerType * +getMostInformativeDerivedClass(const ObjCObjectPointerType *From, + const ObjCObjectPointerType *To, ASTContext &C) { + return getMostInformativeDerivedClassImpl(From, To, To, C); +} + +/// Inputs: +/// \param StaticLowerBound Static lower bound for a symbol. The dynamic lower +/// bound might be the subclass of this type. +/// \param StaticUpperBound A static upper bound for a symbol. +/// \p StaticLowerBound expected to be the subclass of \p StaticUpperBound. +/// \param Current The type that was inferred for a symbol in a previous +/// context. Might be null when this is the first time that inference happens. +/// Precondition: +/// \p StaticLowerBound or \p StaticUpperBound is specialized. If \p Current +/// is not null, it is specialized. +/// Possible cases: +/// (1) The \p Current is null and \p StaticLowerBound <: \p StaticUpperBound +/// (2) \p StaticLowerBound <: \p Current <: \p StaticUpperBound +/// (3) \p Current <: \p StaticLowerBound <: \p StaticUpperBound +/// (4) \p StaticLowerBound <: \p StaticUpperBound <: \p Current +/// Effect: +/// Use getMostInformativeDerivedClass with the upper and lower bound of the +/// set {\p StaticLowerBound, \p Current, \p StaticUpperBound}. The computed +/// lower bound must be specialized. If the result differs from \p Current or +/// \p Current is null, store the result. +static bool +storeWhenMoreInformative(ProgramStateRef &State, SymbolRef Sym, + const ObjCObjectPointerType *const *Current, + const ObjCObjectPointerType *StaticLowerBound, + const ObjCObjectPointerType *StaticUpperBound, + ASTContext &C) { + // Precondition + assert(StaticUpperBound->isSpecialized() || + StaticLowerBound->isSpecialized()); + assert(!Current || (*Current)->isSpecialized()); + + // Case (1) + if (!Current) { + if (StaticUpperBound->isUnspecialized()) { + State = State->set<MostSpecializedTypeArgsMap>(Sym, StaticLowerBound); + return true; + } + // Upper bound is specialized. + const ObjCObjectPointerType *WithMostInfo = + getMostInformativeDerivedClass(StaticUpperBound, StaticLowerBound, C); + State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); + return true; + } + + // Case (3) + if (C.canAssignObjCInterfaces(StaticLowerBound, *Current)) { + return false; + } + + // Case (4) + if (C.canAssignObjCInterfaces(*Current, StaticUpperBound)) { + // The type arguments might not be forwarded at any point of inheritance. + const ObjCObjectPointerType *WithMostInfo = + getMostInformativeDerivedClass(*Current, StaticUpperBound, C); + WithMostInfo = + getMostInformativeDerivedClass(WithMostInfo, StaticLowerBound, C); + if (WithMostInfo == *Current) + return false; + State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); + return true; + } + + // Case (2) + const ObjCObjectPointerType *WithMostInfo = + getMostInformativeDerivedClass(*Current, StaticLowerBound, C); + if (WithMostInfo != *Current) { + State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); + return true; + } + + return false; +} + +/// Type inference based on static type information that is available for the +/// cast and the tracked type information for the given symbol. When the tracked +/// symbol and the destination type of the cast are unrelated, report an error. +void DynamicTypePropagation::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + if (CE->getCastKind() != CK_BitCast) + return; + + QualType OriginType = CE->getSubExpr()->getType(); + QualType DestType = CE->getType(); + + const auto *OrigObjectPtrType = OriginType->getAs<ObjCObjectPointerType>(); + const auto *DestObjectPtrType = DestType->getAs<ObjCObjectPointerType>(); + + if (!OrigObjectPtrType || !DestObjectPtrType) + return; + + ProgramStateRef State = C.getState(); + ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C); + + ASTContext &ASTCtxt = C.getASTContext(); + + // This checker detects the subtyping relationships using the assignment + // rules. In order to be able to do this the kindofness must be stripped + // first. The checker treats every type as kindof type anyways: when the + // tracked type is the subtype of the static type it tries to look up the + // methods in the tracked type first. + OrigObjectPtrType = OrigObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt); + DestObjectPtrType = DestObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt); + + // TODO: erase tracked information when there is a cast to unrelated type + // and everything is unspecialized statically. + if (OrigObjectPtrType->isUnspecialized() && + DestObjectPtrType->isUnspecialized()) + return; + + SymbolRef Sym = State->getSVal(CE, C.getLocationContext()).getAsSymbol(); + if (!Sym) + return; + + // Check which assignments are legal. + bool OrigToDest = + ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, OrigObjectPtrType); + bool DestToOrig = + ASTCtxt.canAssignObjCInterfaces(OrigObjectPtrType, DestObjectPtrType); + const ObjCObjectPointerType *const *TrackedType = + State->get<MostSpecializedTypeArgsMap>(Sym); + + // Downcasts and upcasts handled in an uniform way regardless of being + // explicit. Explicit casts however can happen between mismatched types. + if (isa<ExplicitCastExpr>(CE) && !OrigToDest && !DestToOrig) { + // Mismatched types. If the DestType specialized, store it. Forget the + // tracked type otherwise. + if (DestObjectPtrType->isSpecialized()) { + State = State->set<MostSpecializedTypeArgsMap>(Sym, DestObjectPtrType); + C.addTransition(State, AfterTypeProp); + } else if (TrackedType) { + State = State->remove<MostSpecializedTypeArgsMap>(Sym); + C.addTransition(State, AfterTypeProp); + } + return; + } + + // The tracked type should be the sub or super class of the static destination + // type. When an (implicit) upcast or a downcast happens according to static + // types, and there is no subtyping relationship between the tracked and the + // static destination types, it indicates an error. + if (TrackedType && + !ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, *TrackedType) && + !ASTCtxt.canAssignObjCInterfaces(*TrackedType, DestObjectPtrType)) { + static CheckerProgramPointTag IllegalConv(this, "IllegalConversion"); + ExplodedNode *N = C.addTransition(State, AfterTypeProp, &IllegalConv); + reportGenericsBug(*TrackedType, DestObjectPtrType, N, Sym, C); + return; + } + + // Handle downcasts and upcasts. + + const ObjCObjectPointerType *LowerBound = DestObjectPtrType; + const ObjCObjectPointerType *UpperBound = OrigObjectPtrType; + if (OrigToDest && !DestToOrig) + std::swap(LowerBound, UpperBound); + + // The id type is not a real bound. Eliminate it. + LowerBound = LowerBound->isObjCIdType() ? UpperBound : LowerBound; + UpperBound = UpperBound->isObjCIdType() ? LowerBound : UpperBound; + + if (storeWhenMoreInformative(State, Sym, TrackedType, LowerBound, UpperBound, + ASTCtxt)) { + C.addTransition(State, AfterTypeProp); + } +} + +static const Expr *stripCastsAndSugar(const Expr *E) { + E = E->IgnoreParenImpCasts(); + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) + E = POE->getSyntacticForm()->IgnoreParenImpCasts(); + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) + E = OVE->getSourceExpr()->IgnoreParenImpCasts(); + return E; +} + +static bool isObjCTypeParamDependent(QualType Type) { + // It is illegal to typedef parameterized types inside an interface. Therfore + // an Objective-C type can only be dependent on a type parameter when the type + // parameter structurally present in the type itself. + class IsObjCTypeParamDependentTypeVisitor + : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { + public: + IsObjCTypeParamDependentTypeVisitor() : Result(false) {} + bool VisitTypedefType(const TypedefType *Type) { + if (isa<ObjCTypeParamDecl>(Type->getDecl())) { + Result = true; + return false; + } + return true; + } + + bool Result; + }; + + IsObjCTypeParamDependentTypeVisitor Visitor; + Visitor.TraverseType(Type); + return Visitor.Result; +} + +/// A method might not be available in the interface indicated by the static +/// type. However it might be available in the tracked type. In order to +/// properly substitute the type parameters we need the declaration context of +/// the method. The more specialized the enclosing class of the method is, the +/// more likely that the parameter substitution will be successful. +static const ObjCMethodDecl * +findMethodDecl(const ObjCMessageExpr *MessageExpr, + const ObjCObjectPointerType *TrackedType, ASTContext &ASTCtxt) { + const ObjCMethodDecl *Method = nullptr; + + QualType ReceiverType = MessageExpr->getReceiverType(); + const auto *ReceiverObjectPtrType = + ReceiverType->getAs<ObjCObjectPointerType>(); + + // Do this "devirtualization" on instance and class methods only. Trust the + // static type on super and super class calls. + if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Instance || + MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) { + // When the receiver type is id, Class, or some super class of the tracked + // type, look up the method in the tracked type, not in the receiver type. + // This way we preserve more information. + if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType() || + ASTCtxt.canAssignObjCInterfaces(ReceiverObjectPtrType, TrackedType)) { + const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl(); + // The method might not be found. + Selector Sel = MessageExpr->getSelector(); + Method = InterfaceDecl->lookupInstanceMethod(Sel); + if (!Method) + Method = InterfaceDecl->lookupClassMethod(Sel); + } + } + + // Fallback to statick method lookup when the one based on the tracked type + // failed. + return Method ? Method : MessageExpr->getMethodDecl(); +} + +/// Get the returned ObjCObjectPointerType by a method based on the tracked type +/// information, or null pointer when the returned type is not an +/// ObjCObjectPointerType. +static QualType getReturnTypeForMethod( + const ObjCMethodDecl *Method, ArrayRef<QualType> TypeArgs, + const ObjCObjectPointerType *SelfType, ASTContext &C) { + QualType StaticResultType = Method->getReturnType(); + + // Is the return type declared as instance type? + if (StaticResultType == C.getObjCInstanceType()) + return QualType(SelfType, 0); + + // Check whether the result type depends on a type parameter. + if (!isObjCTypeParamDependent(StaticResultType)) + return QualType(); + + QualType ResultType = StaticResultType.substObjCTypeArgs( + C, TypeArgs, ObjCSubstitutionContext::Result); + + return ResultType; +} + +/// When the receiver has a tracked type, use that type to validate the +/// argumments of the message expression and the return value. +void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = M.getReceiverSVal().getAsSymbol(); + if (!Sym) + return; + + const ObjCObjectPointerType *const *TrackedType = + State->get<MostSpecializedTypeArgsMap>(Sym); + if (!TrackedType) + return; + + // Get the type arguments from tracked type and substitute type arguments + // before do the semantic check. + + ASTContext &ASTCtxt = C.getASTContext(); + const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); + const ObjCMethodDecl *Method = + findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); + + // It is possible to call non-existent methods in Obj-C. + if (!Method) + return; + + Optional<ArrayRef<QualType>> TypeArgs = + (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); + // This case might happen when there is an unspecialized override of a + // specialized method. + if (!TypeArgs) + return; + + for (unsigned i = 0; i < Method->param_size(); i++) { + const Expr *Arg = MessageExpr->getArg(i); + const ParmVarDecl *Param = Method->parameters()[i]; + + QualType OrigParamType = Param->getType(); + if (!isObjCTypeParamDependent(OrigParamType)) + continue; + + QualType ParamType = OrigParamType.substObjCTypeArgs( + ASTCtxt, *TypeArgs, ObjCSubstitutionContext::Parameter); + // Check if it can be assigned + const auto *ParamObjectPtrType = ParamType->getAs<ObjCObjectPointerType>(); + const auto *ArgObjectPtrType = + stripCastsAndSugar(Arg)->getType()->getAs<ObjCObjectPointerType>(); + if (!ParamObjectPtrType || !ArgObjectPtrType) + continue; + + // Check if we have more concrete tracked type that is not a super type of + // the static argument type. + SVal ArgSVal = M.getArgSVal(i); + SymbolRef ArgSym = ArgSVal.getAsSymbol(); + if (ArgSym) { + const ObjCObjectPointerType *const *TrackedArgType = + State->get<MostSpecializedTypeArgsMap>(ArgSym); + if (TrackedArgType && + ASTCtxt.canAssignObjCInterfaces(ArgObjectPtrType, *TrackedArgType)) { + ArgObjectPtrType = *TrackedArgType; + } + } + + // Warn when argument is incompatible with the parameter. + if (!ASTCtxt.canAssignObjCInterfaces(ParamObjectPtrType, + ArgObjectPtrType)) { + static CheckerProgramPointTag Tag(this, "ArgTypeMismatch"); + ExplodedNode *N = C.addTransition(State, &Tag); + reportGenericsBug(ArgObjectPtrType, ParamObjectPtrType, N, Sym, C, Arg); + return; + } + } +} + +/// This callback is used to infer the types for Class variables. This info is +/// used later to validate messages that sent to classes. Class variables are +/// initialized with by invoking the 'class' method on a class. +/// This method is also used to infer the type information for the return +/// types. +// TODO: right now it only tracks generic types. Extend this to track every +// type in the DynamicTypeMap and diagnose type errors! +void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); + + SymbolRef RetSym = M.getReturnValue().getAsSymbol(); + if (!RetSym) + return; + + Selector Sel = MessageExpr->getSelector(); + ProgramStateRef State = C.getState(); + // Inference for class variables. + // We are only interested in cases where the class method is invoked on a + // class. This method is provided by the runtime and available on all classes. + if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class && + Sel.getAsString() == "class") { + + QualType ReceiverType = MessageExpr->getClassReceiver(); + const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>(); + QualType ReceiverClassPointerType = + C.getASTContext().getObjCObjectPointerType( + QualType(ReceiverClassType, 0)); + + if (!ReceiverClassType->isSpecialized()) + return; + const auto *InferredType = + ReceiverClassPointerType->getAs<ObjCObjectPointerType>(); + assert(InferredType); + + State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType); + C.addTransition(State); + return; + } + + // Tracking for return types. + SymbolRef RecSym = M.getReceiverSVal().getAsSymbol(); + if (!RecSym) + return; + + const ObjCObjectPointerType *const *TrackedType = + State->get<MostSpecializedTypeArgsMap>(RecSym); + if (!TrackedType) + return; + + ASTContext &ASTCtxt = C.getASTContext(); + const ObjCMethodDecl *Method = + findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); + if (!Method) + return; + + Optional<ArrayRef<QualType>> TypeArgs = + (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); + if (!TypeArgs) + return; + + QualType ResultType = + getReturnTypeForMethod(Method, *TypeArgs, *TrackedType, ASTCtxt); + // The static type is the same as the deduced type. + if (ResultType.isNull()) + return; + + const MemRegion *RetRegion = M.getReturnValue().getAsRegion(); + ExplodedNode *Pred = C.getPredecessor(); + // When there is an entry available for the return symbol in DynamicTypeMap, + // the call was inlined, and the information in the DynamicTypeMap is should + // be precise. + if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) { + // TODO: we have duplicated information in DynamicTypeMap and + // MostSpecializedTypeArgsMap. We should only store anything in the later if + // the stored data differs from the one stored in the former. + State = setDynamicTypeInfo(State, RetRegion, ResultType, + /*CanBeSubclass=*/true); + Pred = C.addTransition(State); + } + + const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>(); + + if (!ResultPtrType || ResultPtrType->isUnspecialized()) + return; + + // When the result is a specialized type and it is not tracked yet, track it + // for the result symbol. + if (!State->get<MostSpecializedTypeArgsMap>(RetSym)) { + State = State->set<MostSpecializedTypeArgsMap>(RetSym, ResultPtrType); + C.addTransition(State, Pred); + } +} + +void DynamicTypePropagation::reportGenericsBug( + const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, + ExplodedNode *N, SymbolRef Sym, CheckerContext &C, + const Stmt *ReportedNode) const { + if (!CheckGenerics) + return; + + initBugType(); + SmallString<192> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Conversion from value of type '"; + QualType::print(From, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); + OS << "' to incompatible type '"; + QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); + OS << "'"; + std::unique_ptr<BugReport> R( + new BugReport(*ObjCGenericsBugType, OS.str(), N)); + R->markInteresting(Sym); + R->addVisitor(llvm::make_unique<GenericsBugVisitor>(Sym)); + if (ReportedNode) + R->addRange(ReportedNode->getSourceRange()); + C.emitReport(std::move(R)); +} + +PathDiagnosticPiece *DynamicTypePropagation::GenericsBugVisitor::VisitNode( + const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, + BugReport &BR) { + ProgramStateRef state = N->getState(); + ProgramStateRef statePrev = PrevN->getState(); + + const ObjCObjectPointerType *const *TrackedType = + state->get<MostSpecializedTypeArgsMap>(Sym); + const ObjCObjectPointerType *const *TrackedTypePrev = + statePrev->get<MostSpecializedTypeArgsMap>(Sym); + if (!TrackedType) + return nullptr; + + if (TrackedTypePrev && *TrackedTypePrev == *TrackedType) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = nullptr; + ProgramPoint ProgLoc = N->getLocation(); + if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) { + S = SP->getStmt(); + } + + if (!S) + return nullptr; + + const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Type '"; + QualType::print(*TrackedType, Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' is inferred from "; + + if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { + OS << "explicit cast (from '"; + QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { + OS << "implicit cast (from '"; + QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else { + OS << "this context"; + } + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(Pos, OS.str(), true, nullptr); +} + +/// Register checkers. +void ento::registerObjCGenericsChecker(CheckerManager &mgr) { + DynamicTypePropagation *checker = + mgr.registerChecker<DynamicTypePropagation>(); + checker->CheckGenerics = true; +} + void ento::registerDynamicTypePropagation(CheckerManager &mgr) { mgr.registerChecker<DynamicTypePropagation>(); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 7dc0a87..8f6c20a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -17,22 +17,26 @@ using namespace clang; using namespace ento; namespace { -class ExprInspectionChecker : public Checker< eval::Call > { +class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> { mutable std::unique_ptr<BugType> BT; void analyzerEval(const CallExpr *CE, CheckerContext &C) const; void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; + void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, CheckerContext &C) const; public: bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; }; } +REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *) + bool ExprInspectionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // These checks should have no effect on the surrounding environment @@ -42,7 +46,10 @@ bool ExprInspectionChecker::evalCall(const CallExpr *CE, .Case("clang_analyzer_checkInlined", &ExprInspectionChecker::analyzerCheckInlined) .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) - .Case("clang_analyzer_warnIfReached", &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnIfReached", + &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnOnDeadSymbol", + &ExprInspectionChecker::analyzerWarnOnDeadSymbol) .Default(nullptr); if (!Handler) @@ -86,8 +93,7 @@ static const char *getArgumentValueString(const CallExpr *CE, void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { - ExplodedNode *N = C.getPredecessor(); - const LocationContext *LC = N->getLocationContext(); + const LocationContext *LC = C.getPredecessor()->getLocationContext(); // A specific instantiation of an inlined function may have more constrained // values than can generally be assumed. Skip the check. @@ -97,24 +103,28 @@ void ExprInspectionChecker::analyzerEval(const CallExpr *CE, if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; C.emitReport( llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N)); } void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const { - ExplodedNode *N = C.getPredecessor(); if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; C.emitReport(llvm::make_unique<BugReport>(*BT, "REACHABLE", N)); } void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const { - ExplodedNode *N = C.getPredecessor(); - const LocationContext *LC = N->getLocationContext(); + const LocationContext *LC = C.getPredecessor()->getLocationContext(); // An inlined function could conceivably also be analyzed as a top-level // function. We ignore this case and only emit a message (TRUE or FALSE) @@ -127,10 +137,48 @@ void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; C.emitReport( llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N)); } +void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) + return; + SVal Val = C.getSVal(CE->getArg(0)); + SymbolRef Sym = Val.getAsSymbol(); + if (!Sym) + return; + + ProgramStateRef State = C.getState(); + State = State->add<MarkedSymbols>(Sym); + C.addTransition(State); +} + +void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); + for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { + SymbolRef Sym = static_cast<SymbolRef>(*I); + if (!SymReaper.isDead(Sym)) + continue; + + if (!BT) + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + C.emitReport(llvm::make_unique<BugReport>(*BT, "SYMBOL DEAD", N)); + C.addTransition(State->remove<MarkedSymbols>(Sym), N); + } +} + void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, CheckerContext &C) const { LLVM_BUILTIN_TRAP; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 48d6bd4..3fe89f9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -23,7 +23,7 @@ using namespace clang; using namespace ento; namespace { -class FixedAddressChecker +class FixedAddressChecker : public Checker< check::PreStmt<BinaryOperator> > { mutable std::unique_ptr<BuiltinBug> BT; @@ -50,7 +50,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, if (!RV.isConstant() || RV.isZeroConstant()) return; - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Use fixed address", diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 2cf508f..8c8acc6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -100,8 +100,8 @@ private: /// Generate a report if the expression is tainted or points to tainted data. bool generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const; - - + + typedef SmallVector<unsigned, 2> ArgVector; /// \brief A struct used to specify taint propagation rules for a function. @@ -441,7 +441,7 @@ SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, return Val.getAsSymbol(); } -ProgramStateRef +ProgramStateRef GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = C.getState(); @@ -640,7 +640,7 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, return false; // Generate diagnostic. - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, Msg, N); report->addRange(E->getSourceRange()); @@ -658,17 +658,15 @@ bool GenericTaintChecker::checkUncontrolledFormatString(const CallExpr *CE, return false; // If either the format string content or the pointer itself are tainted, warn. - if (generateReportIfTainted(CE->getArg(ArgNum), - MsgUncontrolledFormatString, C)) - return true; - return false; + return generateReportIfTainted(CE->getArg(ArgNum), + MsgUncontrolledFormatString, C); } bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name, CheckerContext &C) const { - // TODO: It might make sense to run this check on demand. In some cases, - // we should check if the environment has been cleansed here. We also might + // TODO: It might make sense to run this check on demand. In some cases, + // we should check if the environment has been cleansed here. We also might // need to know if the user was reset before these calls(seteuid). unsigned ArgNum = llvm::StringSwitch<unsigned>(Name) .Case("system", 0) @@ -686,11 +684,7 @@ bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1)) return false; - if (generateReportIfTainted(CE->getArg(ArgNum), - MsgSanitizeSystemArgs, C)) - return true; - - return false; + return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C); } // TODO: Should this check be a part of the CString checker? @@ -728,11 +722,8 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, ArgNum = 2; } - if (ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum && - generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C)) - return true; - - return false; + return ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum && + generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C); } void ento::registerGenericTaintChecker(CheckerManager &mgr) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp index 58d0783..0c3bff5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -96,7 +96,7 @@ void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B, } LHS = B2->getLHS(); } - + if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) { Sr[0] = RHS->getSourceRange(); Sr[1] = LHS->getSourceRange(); @@ -108,6 +108,24 @@ bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) { const Stmt *Stmt1 = I->getThen(); const Stmt *Stmt2 = I->getElse(); + // Check for identical inner condition: + // + // if (x<10) { + // if (x<10) { + // .. + if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) { + if (!CS->body_empty()) { + const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin()); + if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*ignoreSideEffects=*/ false)) { + PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions", + categories::LogicError, + "conditions of the inner and outer statements are identical", + ELoc); + } + } + } + // Check for identical conditions: // // if (b) { @@ -287,9 +305,7 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, const Stmt *Stmt2, bool IgnoreSideEffects) { if (!Stmt1 || !Stmt2) { - if (!Stmt1 && !Stmt2) - return true; - return false; + return !Stmt1 && !Stmt2; } // If Stmt1 & Stmt2 are of different class then they are not @@ -332,6 +348,7 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, return false; case Stmt::CallExprClass: case Stmt::ArraySubscriptExprClass: + case Stmt::OMPArraySectionExprClass: case Stmt::ImplicitCastExprClass: case Stmt::ParenExprClass: case Stmt::BreakStmtClass: diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index 3df5fa0..dffff38 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -20,8 +20,8 @@ // been called on them. An invalidation method should either invalidate all // the ivars or call another invalidation method (on self). // -// Partial invalidor annotation allows to addess cases when ivars are -// invalidated by other methods, which might or might not be called from +// Partial invalidor annotation allows to addess cases when ivars are +// invalidated by other methods, which might or might not be called from // the invalidation method. The checker checks that each invalidation // method and all the partial methods cumulatively invalidate all ivars. // __attribute__((annotate("objc_instance_variable_invalidator_partial"))); @@ -310,7 +310,7 @@ const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( // Lookup for the synthesized case. IvarD = Prop->getPropertyIvarDecl(); - // We only track the ivars/properties that are defined in the current + // We only track the ivars/properties that are defined in the current // class (not the parent). if (IvarD && IvarD->getContainingInterface() == InterfaceD) { if (TrackedIvars.count(IvarD)) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index 4e3f9b7..db4fbca 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -276,7 +276,6 @@ void ASTFieldVisitor::ReportError(QualType T) { } } 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 diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp new file mode 100644 index 0000000..56346cd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -0,0 +1,1201 @@ +//=- LocalizationChecker.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 set of checks for localizability including: +// 1) A checker that warns about uses of non-localized NSStrings passed to +// UI methods expecting localized strings +// 2) A syntactic checker that warns against the bad practice of +// not including a comment in NSLocalizedString macros. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Lex/Lexer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/Support/Unicode.h" +#include "llvm/ADT/StringSet.h" + +using namespace clang; +using namespace ento; + +namespace { +struct LocalizedState { +private: + enum Kind { NonLocalized, Localized } K; + LocalizedState(Kind InK) : K(InK) {} + +public: + bool isLocalized() const { return K == Localized; } + bool isNonLocalized() const { return K == NonLocalized; } + + static LocalizedState getLocalized() { return LocalizedState(Localized); } + static LocalizedState getNonLocalized() { + return LocalizedState(NonLocalized); + } + + // Overload the == operator + bool operator==(const LocalizedState &X) const { return K == X.K; } + + // LLVMs equivalent of a hash function + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; + +class NonLocalizedStringChecker + : public Checker<check::PostCall, check::PreObjCMessage, + check::PostObjCMessage, + check::PostStmt<ObjCStringLiteral>> { + + mutable std::unique_ptr<BugType> BT; + + // Methods that require a localized string + mutable llvm::DenseMap<const IdentifierInfo *, + llvm::DenseMap<Selector, uint8_t>> UIMethods; + // Methods that return a localized string + mutable llvm::SmallSet<std::pair<const IdentifierInfo *, Selector>, 12> LSM; + // C Functions that return a localized string + mutable llvm::SmallSet<const IdentifierInfo *, 5> LSF; + + void initUIMethods(ASTContext &Ctx) const; + void initLocStringsMethods(ASTContext &Ctx) const; + + bool hasNonLocalizedState(SVal S, CheckerContext &C) const; + bool hasLocalizedState(SVal S, CheckerContext &C) const; + void setNonLocalizedState(SVal S, CheckerContext &C) const; + void setLocalizedState(SVal S, CheckerContext &C) const; + + bool isAnnotatedAsLocalized(const Decl *D) const; + void reportLocalizationError(SVal S, const ObjCMethodCall &M, + CheckerContext &C, int argumentNumber = 0) const; + + int getLocalizedArgumentForSelector(const IdentifierInfo *Receiver, + Selector S) const; + +public: + NonLocalizedStringChecker(); + + // When this parameter is set to true, the checker assumes all + // methods that return NSStrings are unlocalized. Thus, more false + // positives will be reported. + DefaultBool IsAggressive; + + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +}; + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *, + LocalizedState) + +NonLocalizedStringChecker::NonLocalizedStringChecker() { + BT.reset(new BugType(this, "Unlocalizable string", + "Localizability Issue (Apple)")); +} + +#define NEW_RECEIVER(receiver) \ + llvm::DenseMap<Selector, uint8_t> &receiver##M = \ + UIMethods.insert({&Ctx.Idents.get(#receiver), \ + llvm::DenseMap<Selector, uint8_t>()}) \ + .first->second; +#define ADD_NULLARY_METHOD(receiver, method, argument) \ + receiver##M.insert( \ + {Ctx.Selectors.getNullarySelector(&Ctx.Idents.get(#method)), argument}); +#define ADD_UNARY_METHOD(receiver, method, argument) \ + receiver##M.insert( \ + {Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(#method)), argument}); +#define ADD_METHOD(receiver, method_list, count, argument) \ + receiver##M.insert({Ctx.Selectors.getSelector(count, method_list), argument}); + +/// Initializes a list of methods that require a localized string +/// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...} +void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const { + if (!UIMethods.empty()) + return; + + // UI Methods + NEW_RECEIVER(UISearchDisplayController) + ADD_UNARY_METHOD(UISearchDisplayController, setSearchResultsTitle, 0) + + NEW_RECEIVER(UITabBarItem) + IdentifierInfo *initWithTitleUITabBarItemTag[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("tag")}; + ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemTag, 3, 0) + IdentifierInfo *initWithTitleUITabBarItemImage[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("selectedImage")}; + ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemImage, 3, 0) + + NEW_RECEIVER(NSDockTile) + ADD_UNARY_METHOD(NSDockTile, setBadgeLabel, 0) + + NEW_RECEIVER(NSStatusItem) + ADD_UNARY_METHOD(NSStatusItem, setTitle, 0) + ADD_UNARY_METHOD(NSStatusItem, setToolTip, 0) + + NEW_RECEIVER(UITableViewRowAction) + IdentifierInfo *rowActionWithStyleUITableViewRowAction[] = { + &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(UITableViewRowAction, rowActionWithStyleUITableViewRowAction, 3, 1) + ADD_UNARY_METHOD(UITableViewRowAction, setTitle, 0) + + NEW_RECEIVER(NSBox) + ADD_UNARY_METHOD(NSBox, setTitle, 0) + + NEW_RECEIVER(NSButton) + ADD_UNARY_METHOD(NSButton, setTitle, 0) + ADD_UNARY_METHOD(NSButton, setAlternateTitle, 0) + + NEW_RECEIVER(NSSavePanel) + ADD_UNARY_METHOD(NSSavePanel, setPrompt, 0) + ADD_UNARY_METHOD(NSSavePanel, setTitle, 0) + ADD_UNARY_METHOD(NSSavePanel, setNameFieldLabel, 0) + ADD_UNARY_METHOD(NSSavePanel, setNameFieldStringValue, 0) + ADD_UNARY_METHOD(NSSavePanel, setMessage, 0) + + NEW_RECEIVER(UIPrintInfo) + ADD_UNARY_METHOD(UIPrintInfo, setJobName, 0) + + NEW_RECEIVER(NSTabViewItem) + ADD_UNARY_METHOD(NSTabViewItem, setLabel, 0) + ADD_UNARY_METHOD(NSTabViewItem, setToolTip, 0) + + NEW_RECEIVER(NSBrowser) + IdentifierInfo *setTitleNSBrowser[] = {&Ctx.Idents.get("setTitle"), + &Ctx.Idents.get("ofColumn")}; + ADD_METHOD(NSBrowser, setTitleNSBrowser, 2, 0) + + NEW_RECEIVER(UIAccessibilityElement) + ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityLabel, 0) + ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityHint, 0) + ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityValue, 0) + + NEW_RECEIVER(UIAlertAction) + IdentifierInfo *actionWithTitleUIAlertAction[] = { + &Ctx.Idents.get("actionWithTitle"), &Ctx.Idents.get("style"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(UIAlertAction, actionWithTitleUIAlertAction, 3, 0) + + NEW_RECEIVER(NSPopUpButton) + ADD_UNARY_METHOD(NSPopUpButton, addItemWithTitle, 0) + IdentifierInfo *insertItemWithTitleNSPopUpButton[] = { + &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSPopUpButton, insertItemWithTitleNSPopUpButton, 2, 0) + ADD_UNARY_METHOD(NSPopUpButton, removeItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButton, selectItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButton, setTitle, 0) + + NEW_RECEIVER(NSTableViewRowAction) + IdentifierInfo *rowActionWithStyleNSTableViewRowAction[] = { + &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(NSTableViewRowAction, rowActionWithStyleNSTableViewRowAction, 3, 1) + ADD_UNARY_METHOD(NSTableViewRowAction, setTitle, 0) + + NEW_RECEIVER(NSImage) + ADD_UNARY_METHOD(NSImage, setAccessibilityDescription, 0) + + NEW_RECEIVER(NSUserActivity) + ADD_UNARY_METHOD(NSUserActivity, setTitle, 0) + + NEW_RECEIVER(NSPathControlItem) + ADD_UNARY_METHOD(NSPathControlItem, setTitle, 0) + + NEW_RECEIVER(NSCell) + ADD_UNARY_METHOD(NSCell, initTextCell, 0) + ADD_UNARY_METHOD(NSCell, setTitle, 0) + ADD_UNARY_METHOD(NSCell, setStringValue, 0) + + NEW_RECEIVER(NSPathControl) + ADD_UNARY_METHOD(NSPathControl, setPlaceholderString, 0) + + NEW_RECEIVER(UIAccessibility) + ADD_UNARY_METHOD(UIAccessibility, setAccessibilityLabel, 0) + ADD_UNARY_METHOD(UIAccessibility, setAccessibilityHint, 0) + ADD_UNARY_METHOD(UIAccessibility, setAccessibilityValue, 0) + + NEW_RECEIVER(NSTableColumn) + ADD_UNARY_METHOD(NSTableColumn, setTitle, 0) + ADD_UNARY_METHOD(NSTableColumn, setHeaderToolTip, 0) + + NEW_RECEIVER(NSSegmentedControl) + IdentifierInfo *setLabelNSSegmentedControl[] = { + &Ctx.Idents.get("setLabel"), &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedControl, setLabelNSSegmentedControl, 2, 0) + + NEW_RECEIVER(NSButtonCell) + ADD_UNARY_METHOD(NSButtonCell, setTitle, 0) + ADD_UNARY_METHOD(NSButtonCell, setAlternateTitle, 0) + + NEW_RECEIVER(NSSliderCell) + ADD_UNARY_METHOD(NSSliderCell, setTitle, 0) + + NEW_RECEIVER(NSControl) + ADD_UNARY_METHOD(NSControl, setStringValue, 0) + + NEW_RECEIVER(NSAccessibility) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityValueDescription, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityLabel, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityTitle, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityPlaceholderValue, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityHelp, 0) + + NEW_RECEIVER(NSMatrix) + IdentifierInfo *setToolTipNSMatrix[] = {&Ctx.Idents.get("setToolTip"), + &Ctx.Idents.get("forCell")}; + ADD_METHOD(NSMatrix, setToolTipNSMatrix, 2, 0) + + NEW_RECEIVER(NSPrintPanel) + ADD_UNARY_METHOD(NSPrintPanel, setDefaultButtonTitle, 0) + + NEW_RECEIVER(UILocalNotification) + ADD_UNARY_METHOD(UILocalNotification, setAlertBody, 0) + ADD_UNARY_METHOD(UILocalNotification, setAlertAction, 0) + ADD_UNARY_METHOD(UILocalNotification, setAlertTitle, 0) + + NEW_RECEIVER(NSSlider) + ADD_UNARY_METHOD(NSSlider, setTitle, 0) + + NEW_RECEIVER(UIMenuItem) + IdentifierInfo *initWithTitleUIMenuItem[] = {&Ctx.Idents.get("initWithTitle"), + &Ctx.Idents.get("action")}; + ADD_METHOD(UIMenuItem, initWithTitleUIMenuItem, 2, 0) + ADD_UNARY_METHOD(UIMenuItem, setTitle, 0) + + NEW_RECEIVER(UIAlertController) + IdentifierInfo *alertControllerWithTitleUIAlertController[] = { + &Ctx.Idents.get("alertControllerWithTitle"), &Ctx.Idents.get("message"), + &Ctx.Idents.get("preferredStyle")}; + ADD_METHOD(UIAlertController, alertControllerWithTitleUIAlertController, 3, 1) + ADD_UNARY_METHOD(UIAlertController, setTitle, 0) + ADD_UNARY_METHOD(UIAlertController, setMessage, 0) + + NEW_RECEIVER(UIApplicationShortcutItem) + IdentifierInfo *initWithTypeUIApplicationShortcutItemIcon[] = { + &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle"), + &Ctx.Idents.get("localizedSubtitle"), &Ctx.Idents.get("icon"), + &Ctx.Idents.get("userInfo")}; + ADD_METHOD(UIApplicationShortcutItem, + initWithTypeUIApplicationShortcutItemIcon, 5, 1) + IdentifierInfo *initWithTypeUIApplicationShortcutItem[] = { + &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle")}; + ADD_METHOD(UIApplicationShortcutItem, initWithTypeUIApplicationShortcutItem, + 2, 1) + + NEW_RECEIVER(UIActionSheet) + IdentifierInfo *initWithTitleUIActionSheet[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("delegate"), + &Ctx.Idents.get("cancelButtonTitle"), + &Ctx.Idents.get("destructiveButtonTitle"), + &Ctx.Idents.get("otherButtonTitles")}; + ADD_METHOD(UIActionSheet, initWithTitleUIActionSheet, 5, 0) + ADD_UNARY_METHOD(UIActionSheet, addButtonWithTitle, 0) + ADD_UNARY_METHOD(UIActionSheet, setTitle, 0) + + NEW_RECEIVER(NSURLSessionTask) + ADD_UNARY_METHOD(NSURLSessionTask, setTaskDescription, 0) + + NEW_RECEIVER(UIAccessibilityCustomAction) + IdentifierInfo *initWithNameUIAccessibilityCustomAction[] = { + &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), + &Ctx.Idents.get("selector")}; + ADD_METHOD(UIAccessibilityCustomAction, + initWithNameUIAccessibilityCustomAction, 3, 0) + ADD_UNARY_METHOD(UIAccessibilityCustomAction, setName, 0) + + NEW_RECEIVER(UISearchBar) + ADD_UNARY_METHOD(UISearchBar, setText, 0) + ADD_UNARY_METHOD(UISearchBar, setPrompt, 0) + ADD_UNARY_METHOD(UISearchBar, setPlaceholder, 0) + + NEW_RECEIVER(UIBarItem) + ADD_UNARY_METHOD(UIBarItem, setTitle, 0) + + NEW_RECEIVER(UITextView) + ADD_UNARY_METHOD(UITextView, setText, 0) + + NEW_RECEIVER(NSView) + ADD_UNARY_METHOD(NSView, setToolTip, 0) + + NEW_RECEIVER(NSTextField) + ADD_UNARY_METHOD(NSTextField, setPlaceholderString, 0) + + NEW_RECEIVER(NSAttributedString) + ADD_UNARY_METHOD(NSAttributedString, initWithString, 0) + IdentifierInfo *initWithStringNSAttributedString[] = { + &Ctx.Idents.get("initWithString"), &Ctx.Idents.get("attributes")}; + ADD_METHOD(NSAttributedString, initWithStringNSAttributedString, 2, 0) + + NEW_RECEIVER(NSText) + ADD_UNARY_METHOD(NSText, setString, 0) + + NEW_RECEIVER(UIKeyCommand) + IdentifierInfo *keyCommandWithInputUIKeyCommand[] = { + &Ctx.Idents.get("keyCommandWithInput"), &Ctx.Idents.get("modifierFlags"), + &Ctx.Idents.get("action"), &Ctx.Idents.get("discoverabilityTitle")}; + ADD_METHOD(UIKeyCommand, keyCommandWithInputUIKeyCommand, 4, 3) + ADD_UNARY_METHOD(UIKeyCommand, setDiscoverabilityTitle, 0) + + NEW_RECEIVER(UILabel) + ADD_UNARY_METHOD(UILabel, setText, 0) + + NEW_RECEIVER(NSAlert) + IdentifierInfo *alertWithMessageTextNSAlert[] = { + &Ctx.Idents.get("alertWithMessageText"), &Ctx.Idents.get("defaultButton"), + &Ctx.Idents.get("alternateButton"), &Ctx.Idents.get("otherButton"), + &Ctx.Idents.get("informativeTextWithFormat")}; + ADD_METHOD(NSAlert, alertWithMessageTextNSAlert, 5, 0) + ADD_UNARY_METHOD(NSAlert, addButtonWithTitle, 0) + ADD_UNARY_METHOD(NSAlert, setMessageText, 0) + ADD_UNARY_METHOD(NSAlert, setInformativeText, 0) + ADD_UNARY_METHOD(NSAlert, setHelpAnchor, 0) + + NEW_RECEIVER(UIMutableApplicationShortcutItem) + ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedTitle, 0) + ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedSubtitle, 0) + + NEW_RECEIVER(UIButton) + IdentifierInfo *setTitleUIButton[] = {&Ctx.Idents.get("setTitle"), + &Ctx.Idents.get("forState")}; + ADD_METHOD(UIButton, setTitleUIButton, 2, 0) + + NEW_RECEIVER(NSWindow) + ADD_UNARY_METHOD(NSWindow, setTitle, 0) + IdentifierInfo *minFrameWidthWithTitleNSWindow[] = { + &Ctx.Idents.get("minFrameWidthWithTitle"), &Ctx.Idents.get("styleMask")}; + ADD_METHOD(NSWindow, minFrameWidthWithTitleNSWindow, 2, 0) + ADD_UNARY_METHOD(NSWindow, setMiniwindowTitle, 0) + + NEW_RECEIVER(NSPathCell) + ADD_UNARY_METHOD(NSPathCell, setPlaceholderString, 0) + + NEW_RECEIVER(UIDocumentMenuViewController) + IdentifierInfo *addOptionWithTitleUIDocumentMenuViewController[] = { + &Ctx.Idents.get("addOptionWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("order"), &Ctx.Idents.get("handler")}; + ADD_METHOD(UIDocumentMenuViewController, + addOptionWithTitleUIDocumentMenuViewController, 4, 0) + + NEW_RECEIVER(UINavigationItem) + ADD_UNARY_METHOD(UINavigationItem, initWithTitle, 0) + ADD_UNARY_METHOD(UINavigationItem, setTitle, 0) + ADD_UNARY_METHOD(UINavigationItem, setPrompt, 0) + + NEW_RECEIVER(UIAlertView) + IdentifierInfo *initWithTitleUIAlertView[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("message"), + &Ctx.Idents.get("delegate"), &Ctx.Idents.get("cancelButtonTitle"), + &Ctx.Idents.get("otherButtonTitles")}; + ADD_METHOD(UIAlertView, initWithTitleUIAlertView, 5, 0) + ADD_UNARY_METHOD(UIAlertView, addButtonWithTitle, 0) + ADD_UNARY_METHOD(UIAlertView, setTitle, 0) + ADD_UNARY_METHOD(UIAlertView, setMessage, 0) + + NEW_RECEIVER(NSFormCell) + ADD_UNARY_METHOD(NSFormCell, initTextCell, 0) + ADD_UNARY_METHOD(NSFormCell, setTitle, 0) + ADD_UNARY_METHOD(NSFormCell, setPlaceholderString, 0) + + NEW_RECEIVER(NSUserNotification) + ADD_UNARY_METHOD(NSUserNotification, setTitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setSubtitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setInformativeText, 0) + ADD_UNARY_METHOD(NSUserNotification, setActionButtonTitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setOtherButtonTitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setResponsePlaceholder, 0) + + NEW_RECEIVER(NSToolbarItem) + ADD_UNARY_METHOD(NSToolbarItem, setLabel, 0) + ADD_UNARY_METHOD(NSToolbarItem, setPaletteLabel, 0) + ADD_UNARY_METHOD(NSToolbarItem, setToolTip, 0) + + NEW_RECEIVER(NSProgress) + ADD_UNARY_METHOD(NSProgress, setLocalizedDescription, 0) + ADD_UNARY_METHOD(NSProgress, setLocalizedAdditionalDescription, 0) + + NEW_RECEIVER(NSSegmentedCell) + IdentifierInfo *setLabelNSSegmentedCell[] = {&Ctx.Idents.get("setLabel"), + &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedCell, setLabelNSSegmentedCell, 2, 0) + IdentifierInfo *setToolTipNSSegmentedCell[] = {&Ctx.Idents.get("setToolTip"), + &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedCell, setToolTipNSSegmentedCell, 2, 0) + + NEW_RECEIVER(NSUndoManager) + ADD_UNARY_METHOD(NSUndoManager, setActionName, 0) + ADD_UNARY_METHOD(NSUndoManager, undoMenuTitleForUndoActionName, 0) + ADD_UNARY_METHOD(NSUndoManager, redoMenuTitleForUndoActionName, 0) + + NEW_RECEIVER(NSMenuItem) + IdentifierInfo *initWithTitleNSMenuItem[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("action"), + &Ctx.Idents.get("keyEquivalent")}; + ADD_METHOD(NSMenuItem, initWithTitleNSMenuItem, 3, 0) + ADD_UNARY_METHOD(NSMenuItem, setTitle, 0) + ADD_UNARY_METHOD(NSMenuItem, setToolTip, 0) + + NEW_RECEIVER(NSPopUpButtonCell) + IdentifierInfo *initTextCellNSPopUpButtonCell[] = { + &Ctx.Idents.get("initTextCell"), &Ctx.Idents.get("pullsDown")}; + ADD_METHOD(NSPopUpButtonCell, initTextCellNSPopUpButtonCell, 2, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, addItemWithTitle, 0) + IdentifierInfo *insertItemWithTitleNSPopUpButtonCell[] = { + &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSPopUpButtonCell, insertItemWithTitleNSPopUpButtonCell, 2, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, removeItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, selectItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, setTitle, 0) + + NEW_RECEIVER(NSViewController) + ADD_UNARY_METHOD(NSViewController, setTitle, 0) + + NEW_RECEIVER(NSMenu) + ADD_UNARY_METHOD(NSMenu, initWithTitle, 0) + IdentifierInfo *insertItemWithTitleNSMenu[] = { + &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("action"), + &Ctx.Idents.get("keyEquivalent"), &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSMenu, insertItemWithTitleNSMenu, 4, 0) + IdentifierInfo *addItemWithTitleNSMenu[] = { + &Ctx.Idents.get("addItemWithTitle"), &Ctx.Idents.get("action"), + &Ctx.Idents.get("keyEquivalent")}; + ADD_METHOD(NSMenu, addItemWithTitleNSMenu, 3, 0) + ADD_UNARY_METHOD(NSMenu, setTitle, 0) + + NEW_RECEIVER(UIMutableUserNotificationAction) + ADD_UNARY_METHOD(UIMutableUserNotificationAction, setTitle, 0) + + NEW_RECEIVER(NSForm) + ADD_UNARY_METHOD(NSForm, addEntry, 0) + IdentifierInfo *insertEntryNSForm[] = {&Ctx.Idents.get("insertEntry"), + &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSForm, insertEntryNSForm, 2, 0) + + NEW_RECEIVER(NSTextFieldCell) + ADD_UNARY_METHOD(NSTextFieldCell, setPlaceholderString, 0) + + NEW_RECEIVER(NSUserNotificationAction) + IdentifierInfo *actionWithIdentifierNSUserNotificationAction[] = { + &Ctx.Idents.get("actionWithIdentifier"), &Ctx.Idents.get("title")}; + ADD_METHOD(NSUserNotificationAction, + actionWithIdentifierNSUserNotificationAction, 2, 1) + + NEW_RECEIVER(NSURLSession) + ADD_UNARY_METHOD(NSURLSession, setSessionDescription, 0) + + NEW_RECEIVER(UITextField) + ADD_UNARY_METHOD(UITextField, setText, 0) + ADD_UNARY_METHOD(UITextField, setPlaceholder, 0) + + NEW_RECEIVER(UIBarButtonItem) + IdentifierInfo *initWithTitleUIBarButtonItem[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("style"), + &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; + ADD_METHOD(UIBarButtonItem, initWithTitleUIBarButtonItem, 4, 0) + + NEW_RECEIVER(UIViewController) + ADD_UNARY_METHOD(UIViewController, setTitle, 0) + + NEW_RECEIVER(UISegmentedControl) + IdentifierInfo *insertSegmentWithTitleUISegmentedControl[] = { + &Ctx.Idents.get("insertSegmentWithTitle"), &Ctx.Idents.get("atIndex"), + &Ctx.Idents.get("animated")}; + ADD_METHOD(UISegmentedControl, insertSegmentWithTitleUISegmentedControl, 3, 0) + IdentifierInfo *setTitleUISegmentedControl[] = { + &Ctx.Idents.get("setTitle"), &Ctx.Idents.get("forSegmentAtIndex")}; + ADD_METHOD(UISegmentedControl, setTitleUISegmentedControl, 2, 0) +} + +#define LSF_INSERT(function_name) LSF.insert(&Ctx.Idents.get(function_name)); +#define LSM_INSERT_NULLARY(receiver, method_name) \ + LSM.insert({&Ctx.Idents.get(receiver), Ctx.Selectors.getNullarySelector( \ + &Ctx.Idents.get(method_name))}); +#define LSM_INSERT_UNARY(receiver, method_name) \ + LSM.insert({&Ctx.Idents.get(receiver), \ + Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(method_name))}); +#define LSM_INSERT_SELECTOR(receiver, method_list, arguments) \ + LSM.insert({&Ctx.Idents.get(receiver), \ + Ctx.Selectors.getSelector(arguments, method_list)}); + +/// Initializes a list of methods and C functions that return a localized string +void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const { + if (!LSM.empty()) + return; + + IdentifierInfo *LocalizedStringMacro[] = { + &Ctx.Idents.get("localizedStringForKey"), &Ctx.Idents.get("value"), + &Ctx.Idents.get("table")}; + LSM_INSERT_SELECTOR("NSBundle", LocalizedStringMacro, 3) + LSM_INSERT_UNARY("NSDateFormatter", "stringFromDate") + IdentifierInfo *LocalizedStringFromDate[] = { + &Ctx.Idents.get("localizedStringFromDate"), &Ctx.Idents.get("dateStyle"), + &Ctx.Idents.get("timeStyle")}; + LSM_INSERT_SELECTOR("NSDateFormatter", LocalizedStringFromDate, 3) + LSM_INSERT_UNARY("NSNumberFormatter", "stringFromNumber") + LSM_INSERT_NULLARY("UITextField", "text") + LSM_INSERT_NULLARY("UITextView", "text") + LSM_INSERT_NULLARY("UILabel", "text") + + LSF_INSERT("CFDateFormatterCreateStringWithDate"); + LSF_INSERT("CFDateFormatterCreateStringWithAbsoluteTime"); + LSF_INSERT("CFNumberFormatterCreateStringWithNumber"); +} + +/// Checks to see if the method / function declaration includes +/// __attribute__((annotate("returns_localized_nsstring"))) +bool NonLocalizedStringChecker::isAnnotatedAsLocalized(const Decl *D) const { + if (!D) + return false; + return std::any_of( + D->specific_attr_begin<AnnotateAttr>(), + D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { + return Ann->getAnnotation() == "returns_localized_nsstring"; + }); +} + +/// Returns true if the given SVal is marked as Localized in the program state +bool NonLocalizedStringChecker::hasLocalizedState(SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); + if (LS && LS->isLocalized()) + return true; + } + return false; +} + +/// Returns true if the given SVal is marked as NonLocalized in the program +/// state +bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); + if (LS && LS->isNonLocalized()) + return true; + } + return false; +} + +/// Marks the given SVal as Localized in the program state +void NonLocalizedStringChecker::setLocalizedState(const SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + ProgramStateRef State = + C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized()); + C.addTransition(State); + } +} + +/// Marks the given SVal as NonLocalized in the program state +void NonLocalizedStringChecker::setNonLocalizedState(const SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + ProgramStateRef State = C.getState()->set<LocalizedMemMap>( + mt, LocalizedState::getNonLocalized()); + C.addTransition(State); + } +} + +/// Reports a localization error for the passed in method call and SVal +void NonLocalizedStringChecker::reportLocalizationError( + SVal S, const ObjCMethodCall &M, CheckerContext &C, + int argumentNumber) const { + + ExplodedNode *ErrNode = C.getPredecessor(); + static CheckerProgramPointTag Tag("NonLocalizedStringChecker", + "UnlocalizedString"); + ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag); + + if (!ErrNode) + return; + + // Generate the bug report. + std::unique_ptr<BugReport> R(new BugReport( + *BT, "User-facing text should use localized string macro", ErrNode)); + if (argumentNumber) { + R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange()); + } else { + R->addRange(M.getSourceRange()); + } + R->markInteresting(S); + C.emitReport(std::move(R)); +} + +/// Returns the argument number requiring localized string if it exists +/// otherwise, returns -1 +int NonLocalizedStringChecker::getLocalizedArgumentForSelector( + const IdentifierInfo *Receiver, Selector S) const { + auto method = UIMethods.find(Receiver); + + if (method == UIMethods.end()) + return -1; + + auto argumentIterator = method->getSecond().find(S); + + if (argumentIterator == method->getSecond().end()) + return -1; + + int argumentNumber = argumentIterator->getSecond(); + return argumentNumber; +} + +/// Check if the string being passed in has NonLocalized state +void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + initUIMethods(C.getASTContext()); + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); + if (!OD) + return; + const IdentifierInfo *odInfo = OD->getIdentifier(); + + Selector S = msg.getSelector(); + + std::string SelectorString = S.getAsString(); + StringRef SelectorName = SelectorString; + assert(!SelectorName.empty()); + + if (odInfo->isStr("NSString")) { + // Handle the case where the receiver is an NSString + // These special NSString methods draw to the screen + + if (!(SelectorName.startswith("drawAtPoint") || + SelectorName.startswith("drawInRect") || + SelectorName.startswith("drawWithRect"))) + return; + + SVal svTitle = msg.getReceiverSVal(); + + bool isNonLocalized = hasNonLocalizedState(svTitle, C); + + if (isNonLocalized) { + reportLocalizationError(svTitle, msg, C); + } + } + + int argumentNumber = getLocalizedArgumentForSelector(odInfo, S); + // Go up each hierarchy of superclasses and their protocols + while (argumentNumber < 0 && OD->getSuperClass() != nullptr) { + for (const auto *P : OD->all_referenced_protocols()) { + argumentNumber = getLocalizedArgumentForSelector(P->getIdentifier(), S); + if (argumentNumber >= 0) + break; + } + if (argumentNumber < 0) { + OD = OD->getSuperClass(); + argumentNumber = getLocalizedArgumentForSelector(OD->getIdentifier(), S); + } + } + + if (argumentNumber < 0) // There was no match in UIMethods + return; + + SVal svTitle = msg.getArgSVal(argumentNumber); + + if (const ObjCStringRegion *SR = + dyn_cast_or_null<ObjCStringRegion>(svTitle.getAsRegion())) { + StringRef stringValue = + SR->getObjCStringLiteral()->getString()->getString(); + if ((stringValue.trim().size() == 0 && stringValue.size() > 0) || + stringValue.empty()) + return; + if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2) + return; + } + + bool isNonLocalized = hasNonLocalizedState(svTitle, C); + + if (isNonLocalized) { + reportLocalizationError(svTitle, msg, C, argumentNumber + 1); + } +} + +static inline bool isNSStringType(QualType T, ASTContext &Ctx) { + + const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); + if (!PT) + return false; + + ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); + if (!Cls) + return false; + + IdentifierInfo *ClsName = Cls->getIdentifier(); + + // FIXME: Should we walk the chain of classes? + return ClsName == &Ctx.Idents.get("NSString") || + ClsName == &Ctx.Idents.get("NSMutableString"); +} + +/// Marks a string being returned by any call as localized +/// if it is in LocStringFunctions (LSF) or the function is annotated. +/// Otherwise, we mark it as NonLocalized (Aggressive) or +/// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive), +/// basically leaving only string literals as NonLocalized. +void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + initLocStringsMethods(C.getASTContext()); + + if (!Call.getOriginExpr()) + return; + + // Anything that takes in a localized NSString as an argument + // and returns an NSString will be assumed to be returning a + // localized NSString. (Counter: Incorrectly combining two LocalizedStrings) + const QualType RT = Call.getResultType(); + if (isNSStringType(RT, C.getASTContext())) { + for (unsigned i = 0; i < Call.getNumArgs(); ++i) { + SVal argValue = Call.getArgSVal(i); + if (hasLocalizedState(argValue, C)) { + SVal sv = Call.getReturnValue(); + setLocalizedState(sv, C); + return; + } + } + } + + const Decl *D = Call.getDecl(); + if (!D) + return; + + const IdentifierInfo *Identifier = Call.getCalleeIdentifier(); + + SVal sv = Call.getReturnValue(); + if (isAnnotatedAsLocalized(D) || LSF.count(Identifier) != 0) { + setLocalizedState(sv, C); + } else if (isNSStringType(RT, C.getASTContext()) && + !hasLocalizedState(sv, C)) { + if (IsAggressive) { + setNonLocalizedState(sv, C); + } else { + const SymbolicRegion *SymReg = + dyn_cast_or_null<SymbolicRegion>(sv.getAsRegion()); + if (!SymReg) + setNonLocalizedState(sv, C); + } + } +} + +/// Marks a string being returned by an ObjC method as localized +/// if it is in LocStringMethods or the method is annotated +void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + initLocStringsMethods(C.getASTContext()); + + if (!msg.isInstanceMessage()) + return; + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); + if (!OD) + return; + const IdentifierInfo *odInfo = OD->getIdentifier(); + + Selector S = msg.getSelector(); + std::string SelectorName = S.getAsString(); + + std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S}; + + if (LSM.count(MethodDescription) || isAnnotatedAsLocalized(msg.getDecl())) { + SVal sv = msg.getReturnValue(); + setLocalizedState(sv, C); + } +} + +/// Marks all empty string literals as localized +void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL, + CheckerContext &C) const { + SVal sv = C.getSVal(SL); + setNonLocalizedState(sv, C); +} + +namespace { +class EmptyLocalizationContextChecker + : public Checker<check::ASTDecl<ObjCImplementationDecl>> { + + // A helper class, which walks the AST + class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { + const ObjCMethodDecl *MD; + BugReporter &BR; + AnalysisManager &Mgr; + const CheckerBase *Checker; + LocationOrAnalysisDeclContext DCtx; + + public: + MethodCrawler(const ObjCMethodDecl *InMD, BugReporter &InBR, + const CheckerBase *Checker, AnalysisManager &InMgr, + AnalysisDeclContext *InDCtx) + : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {} + + void VisitStmt(const Stmt *S) { VisitChildren(S); } + + void VisitObjCMessageExpr(const ObjCMessageExpr *ME); + + void reportEmptyContextError(const ObjCMessageExpr *M) const; + + void VisitChildren(const Stmt *S) { + for (const Stmt *Child : S->children()) { + if (Child) + this->Visit(Child); + } + } + }; + +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +}; +} // end anonymous namespace + +void EmptyLocalizationContextChecker::checkASTDecl( + const ObjCImplementationDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + + for (const ObjCMethodDecl *M : D->methods()) { + AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); + + const Stmt *Body = M->getBody(); + assert(Body); + + MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx); + MC.VisitStmt(Body); + } +} + +/// This check attempts to match these macros, assuming they are defined as +/// follows: +/// +/// #define NSLocalizedString(key, comment) \ +/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil] +/// #define NSLocalizedStringFromTable(key, tbl, comment) \ +/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)] +/// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ +/// [bundle localizedStringForKey:(key) value:@"" table:(tbl)] +/// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) +/// +/// We cannot use the path sensitive check because the macro argument we are +/// checking for (comment) is not used and thus not present in the AST, +/// so we use Lexer on the original macro call and retrieve the value of +/// the comment. If it's empty or nil, we raise a warning. +void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( + const ObjCMessageExpr *ME) { + + const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); + if (!OD) + return; + + const IdentifierInfo *odInfo = OD->getIdentifier(); + + if (!(odInfo->isStr("NSBundle") && + ME->getSelector().getAsString() == + "localizedStringForKey:value:table:")) { + return; + } + + SourceRange R = ME->getSourceRange(); + if (!R.getBegin().isMacroID()) + return; + + // getImmediateMacroCallerLoc gets the location of the immediate macro + // caller, one level up the stack toward the initial macro typed into the + // source, so SL should point to the NSLocalizedString macro. + SourceLocation SL = + Mgr.getSourceManager().getImmediateMacroCallerLoc(R.getBegin()); + std::pair<FileID, unsigned> SLInfo = + Mgr.getSourceManager().getDecomposedLoc(SL); + + SrcMgr::SLocEntry SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); + + // If NSLocalizedString macro is wrapped in another macro, we need to + // unwrap the expansion until we get to the NSLocalizedStringMacro. + while (SE.isExpansion()) { + SL = SE.getExpansion().getSpellingLoc(); + SLInfo = Mgr.getSourceManager().getDecomposedLoc(SL); + SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); + } + + llvm::MemoryBuffer *BF = SE.getFile().getContentCache()->getRawBuffer(); + Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(), + BF->getBufferStart() + SLInfo.second, BF->getBufferEnd()); + + Token I; + Token Result; // This will hold the token just before the last ')' + int p_count = 0; // This is for parenthesis matching + while (!TheLexer.LexFromRawLexer(I)) { + if (I.getKind() == tok::l_paren) + ++p_count; + if (I.getKind() == tok::r_paren) { + if (p_count == 1) + break; + --p_count; + } + Result = I; + } + + if (isAnyIdentifier(Result.getKind())) { + if (Result.getRawIdentifier().equals("nil")) { + reportEmptyContextError(ME); + return; + } + } + + if (!isStringLiteral(Result.getKind())) + return; + + StringRef Comment = + StringRef(Result.getLiteralData(), Result.getLength()).trim("\""); + + if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace + Comment.empty()) { + reportEmptyContextError(ME); + } +} + +void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError( + const ObjCMessageExpr *ME) const { + // Generate the bug report. + BR.EmitBasicReport(MD, Checker, "Context Missing", + "Localizability Issue (Apple)", + "Localized string macro should include a non-empty " + "comment for translators", + PathDiagnosticLocation(ME, BR.getSourceManager(), DCtx)); +} + +namespace { +class PluralMisuseChecker : public Checker<check::ASTCodeBody> { + + // A helper class, which walks the AST + class MethodCrawler : public RecursiveASTVisitor<MethodCrawler> { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext *AC; + + // This functions like a stack. We push on any IfStmt or + // ConditionalOperator that matches the condition + // and pop it off when we leave that statement + llvm::SmallVector<const clang::Stmt *, 8> MatchingStatements; + // This is true when we are the direct-child of a + // matching statement + bool InMatchingStatement = false; + + public: + explicit MethodCrawler(BugReporter &InBR, const CheckerBase *Checker, + AnalysisDeclContext *InAC) + : BR(InBR), Checker(Checker), AC(InAC) {} + + bool VisitIfStmt(const IfStmt *I); + bool EndVisitIfStmt(IfStmt *I); + bool TraverseIfStmt(IfStmt *x); + bool VisitConditionalOperator(const ConditionalOperator *C); + bool TraverseConditionalOperator(ConditionalOperator *C); + bool VisitCallExpr(const CallExpr *CE); + bool VisitObjCMessageExpr(const ObjCMessageExpr *ME); + + private: + void reportPluralMisuseError(const Stmt *S) const; + bool isCheckingPlurality(const Expr *E) const; + }; + +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + MethodCrawler Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); + Visitor.TraverseDecl(const_cast<Decl *>(D)); + } +}; +} // end anonymous namespace + +// Checks the condition of the IfStmt and returns true if one +// of the following heuristics are met: +// 1) The conidtion is a variable with "singular" or "plural" in the name +// 2) The condition is a binary operator with 1 or 2 on the right-hand side +bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality( + const Expr *Condition) const { + const BinaryOperator *BO = nullptr; + // Accounts for when a VarDecl represents a BinaryOperator + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Condition)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + const Expr *InitExpr = VD->getInit(); + if (InitExpr) { + if (const BinaryOperator *B = + dyn_cast<BinaryOperator>(InitExpr->IgnoreParenImpCasts())) { + BO = B; + } + } + if (VD->getName().lower().find("plural") != StringRef::npos || + VD->getName().lower().find("singular") != StringRef::npos) { + return true; + } + } + } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) { + BO = B; + } + + if (BO == nullptr) + return false; + + if (IntegerLiteral *IL = dyn_cast_or_null<IntegerLiteral>( + BO->getRHS()->IgnoreParenImpCasts())) { + llvm::APInt Value = IL->getValue(); + if (Value == 1 || Value == 2) { + return true; + } + } + return false; +} + +// A CallExpr with "LOC" in its identifier that takes in a string literal +// has been shown to almost always be a function that returns a localized +// string. Raise a diagnostic when this is in a statement that matches +// the condition. +bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(const CallExpr *CE) { + if (InMatchingStatement) { + if (const FunctionDecl *FD = CE->getDirectCallee()) { + std::string NormalizedName = + StringRef(FD->getNameInfo().getAsString()).lower(); + if (NormalizedName.find("loc") != std::string::npos) { + for (const Expr *Arg : CE->arguments()) { + if (isa<ObjCStringLiteral>(Arg)) + reportPluralMisuseError(CE); + } + } + } + } + return true; +} + +// The other case is for NSLocalizedString which also returns +// a localized string. It's a macro for the ObjCMessageExpr +// [NSBundle localizedStringForKey:value:table:] Raise a +// diagnostic when this is in a statement that matches +// the condition. +bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr( + const ObjCMessageExpr *ME) { + const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); + if (!OD) + return true; + + const IdentifierInfo *odInfo = OD->getIdentifier(); + + if (odInfo->isStr("NSBundle") && + ME->getSelector().getAsString() == "localizedStringForKey:value:table:") { + if (InMatchingStatement) { + reportPluralMisuseError(ME); + } + } + return true; +} + +/// Override TraverseIfStmt so we know when we are done traversing an IfStmt +bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) { + RecursiveASTVisitor<MethodCrawler>::TraverseIfStmt(I); + return EndVisitIfStmt(I); +} + +// EndVisit callbacks are not provided by the RecursiveASTVisitor +// so we override TraverseIfStmt and make a call to EndVisitIfStmt +// after traversing the IfStmt +bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) { + MatchingStatements.pop_back(); + if (!MatchingStatements.empty()) { + if (MatchingStatements.back() != nullptr) { + InMatchingStatement = true; + return true; + } + } + InMatchingStatement = false; + return true; +} + +bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) { + const Expr *Condition = I->getCond()->IgnoreParenImpCasts(); + if (isCheckingPlurality(Condition)) { + MatchingStatements.push_back(I); + InMatchingStatement = true; + } else { + MatchingStatements.push_back(nullptr); + InMatchingStatement = false; + } + + return true; +} + +// Preliminary support for conditional operators. +bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator( + ConditionalOperator *C) { + RecursiveASTVisitor<MethodCrawler>::TraverseConditionalOperator(C); + MatchingStatements.pop_back(); + if (!MatchingStatements.empty()) { + if (MatchingStatements.back() != nullptr) + InMatchingStatement = true; + else + InMatchingStatement = false; + } else { + InMatchingStatement = false; + } + return true; +} + +bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator( + const ConditionalOperator *C) { + const Expr *Condition = C->getCond()->IgnoreParenImpCasts(); + if (isCheckingPlurality(Condition)) { + MatchingStatements.push_back(C); + InMatchingStatement = true; + } else { + MatchingStatements.push_back(nullptr); + InMatchingStatement = false; + } + return true; +} + +void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError( + const Stmt *S) const { + // Generate the bug report. + BR.EmitBasicReport(AC->getDecl(), Checker, "Plural Misuse", + "Localizability Issue (Apple)", + "Plural cases are not supported accross all languages. " + "Use a .stringsdict file instead", + PathDiagnosticLocation(S, BR.getSourceManager(), AC)); +} + +//===----------------------------------------------------------------------===// +// Checker registration. +//===----------------------------------------------------------------------===// + +void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) { + NonLocalizedStringChecker *checker = + mgr.registerChecker<NonLocalizedStringChecker>(); + checker->IsAggressive = + mgr.getAnalyzerOptions().getBooleanOption("AggressiveReport", false); +} + +void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) { + mgr.registerChecker<EmptyLocalizationContextChecker>(); +} + +void ento::registerPluralMisuseChecker(CheckerManager &mgr) { + mgr.registerChecker<PluralMisuseChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 7838901..1e56d70 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -118,7 +118,7 @@ private: SValBuilder &Builder) const { return definitelyReturnedError(RetSym, State, Builder, true); } - + /// Mark an AllocationPair interesting for diagnostic reporting. void markInteresting(BugReport *R, const AllocationPair &AP) const { R->markInteresting(AP.first); @@ -136,7 +136,6 @@ private: public: SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} - ~SecKeychainBugVisitor() override {} void Profile(llvm::FoldingSetNodeID &ID) const override { static int X = 0; @@ -202,12 +201,8 @@ unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, static bool isBadDeallocationArgument(const MemRegion *Arg) { if (!Arg) return false; - if (isa<AllocaRegion>(Arg) || - isa<BlockDataRegion>(Arg) || - isa<TypedRegion>(Arg)) { - return true; - } - return false; + return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || + isa<TypedRegion>(Arg); } /// Given the address expression, retrieve the value it's pointing to. Assume @@ -241,11 +236,7 @@ bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, nonloc::SymbolVal(RetSym)); ProgramStateRef ErrState = State->assume(NoErr, noError); - if (ErrState == State) { - return true; - } - - return false; + return ErrState == State; } // Report deallocator mismatch. Remove the region from tracking - reporting a @@ -256,7 +247,7 @@ void MacOSKeychainAPIChecker:: CheckerContext &C) const { ProgramStateRef State = C.getState(); State = State->remove<AllocatedData>(AP.first); - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; @@ -283,7 +274,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; - + StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; @@ -302,7 +293,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, // Remove the value from the state. The new symbol will be added for // tracking when the second allocator is processed in checkPostStmt(). State = State->remove<AllocatedData>(V); - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; initBugType(); @@ -365,7 +356,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, if (isEnclosingFunctionParam(ArgExpr)) return; - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; initBugType(); @@ -431,7 +422,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, // report a bad call to free. if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; initBugType(); @@ -585,10 +576,12 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, } static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); - ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); + ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (!N) + return; // Generate the error reports. - for (const auto P : Errors) + for (const auto &P : Errors) C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); // Generate the new, cleaned up state. diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 11ba609..4cbe97b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -62,7 +62,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; - ExplodedNode *N = C.generateSink(state); + ExplodedNode *N = C.generateErrorNode(state); if (!N) return; @@ -79,7 +79,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (TrimmedFName != FName) FName = TrimmedFName; } - + SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << FName << "' uses"; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a9e0865..713d9fe 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -65,10 +65,10 @@ class RefState { const Stmt *S; unsigned K : 3; // Kind enum, but stored as a bitfield. - unsigned Family : 29; // Rest of 32-bit word, currently just an allocation + unsigned Family : 29; // Rest of 32-bit word, currently just an allocation // family. - RefState(Kind k, const Stmt *s, unsigned family) + RefState(Kind k, const Stmt *s, unsigned family) : S(s), K(k), Family(family) { assert(family != AF_None); } @@ -94,7 +94,7 @@ public: return RefState(AllocatedOfSizeZero, RS->getStmt(), RS->getAllocationFamily()); } - static RefState getReleased(unsigned family, const Stmt *s) { + static RefState getReleased(unsigned family, const Stmt *s) { return RefState(Released, s, family); } static RefState getRelinquished(unsigned family, const Stmt *s) { @@ -169,9 +169,9 @@ class MallocChecker : public Checker<check::DeadSymbols, { public: MallocChecker() - : II_alloca(nullptr), II_malloc(nullptr), II_free(nullptr), + : II_alloca(nullptr), II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr), - II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr), + II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr), II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {} @@ -185,7 +185,7 @@ public: CK_NumCheckKinds }; - enum class MemoryOperationKind { + enum class MemoryOperationKind { MOK_Allocate, MOK_Free, MOK_Any @@ -245,19 +245,19 @@ private: /// \brief Print names of allocators and deallocators. /// /// \returns true on success. - bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, const Expr *E) const; /// \brief Print expected name of an allocator based on the deallocator's /// family derived from the DeallocExpr. - void printExpectedAllocName(raw_ostream &os, CheckerContext &C, + void printExpectedAllocName(raw_ostream &os, CheckerContext &C, const Expr *DeallocExpr) const; - /// \brief Print expected name of a deallocator based on the allocator's + /// \brief Print expected name of a deallocator based on the allocator's /// family. void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const; ///@{ - /// Check if this is one of the functions which can allocate/reallocate memory + /// Check if this is one of the functions which can allocate/reallocate memory /// pointed to by one of its arguments. bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; bool isCMemFunction(const FunctionDecl *FD, @@ -292,7 +292,7 @@ private: const ProgramStateRef &State) const; /// Update the RefState to reflect the new memory allocation. - static ProgramStateRef + static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, AllocationFamily Family = AF_Malloc); @@ -312,17 +312,17 @@ private: bool ReturnsNullOnFailure = false) const; ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE, - bool FreesMemOnFailure, + bool FreesMemOnFailure, ProgramStateRef State) const; static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, ProgramStateRef State); - + ///\brief Check if the memory associated with this symbol was released. bool isReleased(SymbolRef Sym, CheckerContext &C) const; bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; - void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, + void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const; @@ -330,7 +330,7 @@ private: /// Check if the function is known free memory, or if it is /// "interesting" and should be modeled explicitly. /// - /// \param [out] EscapingSymbol A function might not free memory in general, + /// \param [out] EscapingSymbol A function might not free memory in general, /// but could be known to free a particular symbol. In this case, false is /// returned and the single escaping symbol is returned through the out /// parameter. @@ -357,20 +357,20 @@ private: Optional<CheckKind> getCheckIfTracked(CheckerContext &C, const Stmt *AllocDeallocStmt, bool IsALeakCheck = false) const; - Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, + Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, bool IsALeakCheck = false) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); - void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const; void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const; void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, const RefState *RS, SymbolRef Sym, bool OwnershipTransferred) const; - void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, - const Expr *DeallocExpr, + void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *DeallocExpr, const Expr *AllocExpr = nullptr) const; void ReportUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const; @@ -392,7 +392,8 @@ private: /// The bug visitor which allows us to print extra diagnostics along the /// BugReport path. For example, showing the allocation site of the leaked /// region. - class MallocBugVisitor : public BugReporterVisitorImpl<MallocBugVisitor> { + class MallocBugVisitor final + : public BugReporterVisitorImpl<MallocBugVisitor> { protected: enum NotificationMode { Normal, @@ -414,8 +415,6 @@ private: MallocBugVisitor(SymbolRef S, bool isLeak = false) : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {} - ~MallocBugVisitor() override {} - void Profile(llvm::FoldingSetNodeID &ID) const override { static int X = 0; ID.AddPointer(&X); @@ -426,8 +425,8 @@ private: const Stmt *Stmt) { // Did not track -> allocated. Other state (released) -> allocated. return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && - (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && - (!SPrev || !(SPrev->isAllocated() || + (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && + (!SPrev || !(SPrev->isAllocated() || SPrev->isAllocatedOfSizeZero()))); } @@ -509,13 +508,14 @@ private: REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) +REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) -// A map from the freed symbol to the symbol representing the return value of +// A map from the freed symbol to the symbol representing the return value of // the free function. REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef) namespace { -class StopTrackingCallback : public SymbolVisitor { +class StopTrackingCallback final : public SymbolVisitor { ProgramStateRef state; public: StopTrackingCallback(ProgramStateRef st) : state(st) {} @@ -634,7 +634,7 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, return false; OverloadedOperatorKind Kind = FD->getOverloadedOperator(); - if (Kind != OO_New && Kind != OO_Array_New && + if (Kind != OO_New && Kind != OO_Array_New && Kind != OO_Delete && Kind != OO_Array_Delete) return false; @@ -799,8 +799,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = ProcessZeroAllocation(C, CE, 0, State); } else if (isStandardNewDelete(FD, C.getASTContext())) { // Process direct calls to operator new/new[]/delete/delete[] functions - // as distinct from new/new[]/delete/delete[] expressions that are - // processed by the checkPostStmt callbacks for CXXNewExpr and + // as distinct from new/new[]/delete/delete[] expressions that are + // processed by the checkPostStmt callbacks for CXXNewExpr and // CXXDeleteExpr. OverloadedOperatorKind K = FD->getOverloadedOperator(); if (K == OO_New) { @@ -870,7 +870,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C, assert(Arg); - Optional<DefinedSVal> DefArgVal = + Optional<DefinedSVal> DefArgVal = State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>(); if (!DefArgVal) @@ -882,7 +882,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C, DefinedSVal Zero = SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); - std::tie(TrueState, FalseState) = + std::tie(TrueState, FalseState) = State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero)); if (TrueState && !FalseState) { @@ -892,15 +892,19 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C, return State; const RefState *RS = State->get<RegionState>(Sym); - if (!RS) - return State; // TODO: change to assert(RS); after realloc() will - // guarantee have a RegionState attached. - - if (!RS->isAllocated()) - return State; - - return TrueState->set<RegionState>(Sym, - RefState::getAllocatedOfSizeZero(RS)); + if (RS) { + if (RS->isAllocated()) + return TrueState->set<RegionState>(Sym, + RefState::getAllocatedOfSizeZero(RS)); + else + return State; + } else { + // Case of zero-size realloc. Historically 'realloc(ptr, 0)' is treated as + // 'free(ptr)' and the returned value from 'realloc(ptr, 0)' is not + // tracked. Add zero-reallocated Sym to the state to catch references + // to zero-allocated memory. + return TrueState->add<ReallocSizeZeroSymbols>(Sym); + } } // Assume the value is non-zero going forward. @@ -944,7 +948,7 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { return false; } -void MallocChecker::checkPostStmt(const CXXNewExpr *NE, +void MallocChecker::checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { if (NE->getNumPlacementArgs()) @@ -961,17 +965,17 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE, return; ProgramStateRef State = C.getState(); - // The return value from operator new is bound to a specified initialization - // value (if any) and we don't want to loose this value. So we call - // MallocUpdateRefState() instead of MallocMemAux() which breakes the + // The return value from operator new is bound to a specified initialization + // value (if any) and we don't want to loose this value. So we call + // MallocUpdateRefState() instead of MallocMemAux() which breakes the // existing binding. - State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray + State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray : AF_CXXNew); State = ProcessZeroAllocation(C, NE, 0, State); C.addTransition(State); } -void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, +void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const { if (!ChecksEnabled[CK_NewDeleteChecker]) @@ -996,12 +1000,9 @@ static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; // (...unless a 'freeWhenDone' parameter is false, but that's checked later.) StringRef FirstSlot = Call.getSelector().getNameForSlot(0); - if (FirstSlot == "dataWithBytesNoCopy" || - FirstSlot == "initWithBytesNoCopy" || - FirstSlot == "initWithCharactersNoCopy") - return true; - - return false; + return FirstSlot == "dataWithBytesNoCopy" || + FirstSlot == "initWithBytesNoCopy" || + FirstSlot == "initWithCharactersNoCopy"; } static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { @@ -1038,7 +1039,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr *Att, + const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) return nullptr; @@ -1105,7 +1106,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, State = State->assume(extentMatchesSize, true); assert(State); } - + return MallocUpdateRefState(C, CE, State, Family); } @@ -1132,7 +1133,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr *Att, + const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) return nullptr; @@ -1184,7 +1185,7 @@ static bool didPreviousFreeFail(ProgramStateRef State, return false; } -AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, +AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, const Stmt *S) const { if (!S) return AF_None; @@ -1229,14 +1230,14 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_None; } -bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, +bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, const Expr *E) const { if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { // FIXME: This doesn't handle indirect calls. const FunctionDecl *FD = CE->getDirectCallee(); if (!FD) return false; - + os << *FD; if (!FD->isOverloadedOperator()) os << "()"; @@ -1253,14 +1254,14 @@ bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, } if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { - os << "'" + os << "'" << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator()) << "'"; return true; } if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) { - os << "'" + os << "'" << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator()) << "'"; return true; @@ -1283,7 +1284,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, } } -void MallocChecker::printExpectedDeallocName(raw_ostream &os, +void MallocChecker::printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const { switch(Family) { case AF_Malloc: os << "free()"; return; @@ -1327,25 +1328,25 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, return nullptr; 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(), ParentExpr); return nullptr; } - + 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(), ParentExpr); return nullptr; } - + const MemSpaceRegion *MS = R->getMemorySpace(); - - // Parameters, locals, statics, globals, and memory returned by + + // Parameters, locals, statics, globals, and memory returned by // __builtin_alloca() shouldn't be freed. if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) { // FIXME: at the time this code was written, malloc() regions were @@ -1391,7 +1392,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // If the pointer is allocated or escaped, but we are now trying to free it, // check that the call to free is proper. - } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() || + } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() || RsBase->isEscaped()) { // Check if an expected deallocation function matches the real one. @@ -1410,20 +1411,20 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, !Offset.hasSymbolicOffset() && Offset.getOffset() != 0) { const Expr *AllocExpr = cast<Expr>(RsBase->getStmt()); - ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, AllocExpr); return nullptr; } } } - ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() || + ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero()); // Clean out the info on previous call to free return info. State = State->remove<FreeReturnValue>(SymBase); - // Keep track of the return value. If it is NULL, we will know that free + // Keep track of the return value. If it is NULL, we will know that free // failed. if (ReturnsNullOnFailure) { SVal RetVal = C.getSVal(ParentExpr); @@ -1463,7 +1464,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, if (IsALeakCheck) { if (ChecksEnabled[CK_NewDeleteLeaksChecker]) return CK_NewDeleteLeaksChecker; - } + } else { if (ChecksEnabled[CK_NewDeleteChecker]) return CK_NewDeleteChecker; @@ -1488,6 +1489,9 @@ MallocChecker::getCheckIfTracked(CheckerContext &C, Optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, bool IsALeakCheck) const { + if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) + return CK_MallocChecker; + const RefState *RS = C.getState()->get<RegionState>(Sym); assert(RS); return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck); @@ -1502,7 +1506,7 @@ bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { os << "the address of the label '" << Label->getLabel()->getName() << "'"; else return false; - + return true; } @@ -1526,7 +1530,7 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, return true; default: { const MemSpaceRegion *MS = MR->getMemorySpace(); - + if (isa<StackLocalsSpaceRegion>(MS)) { const VarRegion *VR = dyn_cast<VarRegion>(MR); const VarDecl *VD; @@ -1580,8 +1584,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, } } -void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, - SourceRange Range, +void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, const Expr *DeallocExpr) const { if (!ChecksEnabled[CK_MallocChecker] && @@ -1593,7 +1597,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_BadFree[*CheckKind]) BT_BadFree[*CheckKind].reset( new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error")); @@ -1610,7 +1614,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, os << "deallocator"; os << " is "; - bool Summarized = MR ? SummarizeRegion(os, MR) + bool Summarized = MR ? SummarizeRegion(os, MR) : SummarizeValue(os, ArgVal); if (Summarized) os << ", which is not memory allocated by "; @@ -1626,7 +1630,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, } } -void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, +void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const { Optional<MallocChecker::CheckKind> CheckKind; @@ -1638,7 +1642,7 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, else return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_FreeAlloca[*CheckKind]) BT_FreeAlloca[*CheckKind].reset( new BugType(CheckNames[*CheckKind], "Free alloca()", "Memory Error")); @@ -1652,17 +1656,17 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, } } -void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, +void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, - const Expr *DeallocExpr, + const Expr *DeallocExpr, const RefState *RS, - SymbolRef Sym, + SymbolRef Sym, bool OwnershipTransferred) const { if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_MismatchedDealloc) BT_MismatchedDealloc.reset( new BugType(CheckNames[CK_MismatchedDeallocatorChecker], @@ -1680,7 +1684,7 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, if (OwnershipTransferred) { if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) os << DeallocOs.str() << " cannot"; - else + else os << "Cannot"; os << " take ownership of memory"; @@ -1721,7 +1725,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, if (!CheckKind.hasValue()) return; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -1775,7 +1779,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_UseFree[*CheckKind]) BT_UseFree[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Use-after-free", "Memory Error")); @@ -1791,7 +1795,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, } void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, - bool Released, SymbolRef Sym, + bool Released, SymbolRef Sym, SymbolRef PrevSym) const { if (!ChecksEnabled[CK_MallocChecker] && @@ -1802,7 +1806,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_DoubleFree[*CheckKind]) BT_DoubleFree[*CheckKind].reset( new BugType(CheckNames[*CheckKind], "Double free", "Memory Error")); @@ -1830,7 +1834,7 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_DoubleDelete) BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker], "Double delete", "Memory Error")); @@ -1857,7 +1861,7 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C, if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_UseZerroAllocated[*CheckKind]) BT_UseZerroAllocated[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Use of zero allocated", "Memory Error")); @@ -1921,7 +1925,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull; bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero; - // If the ptr is NULL and the size is not 0, the call is equivalent to + // If the ptr is NULL and the size is not 0, the call is equivalent to // malloc(size). if ( PrtIsNull && !SizeIsZero) { ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1), @@ -1930,7 +1934,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, } if (PrtIsNull && SizeIsZero) - return nullptr; + return State; // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size). assert(!PrtIsNull); @@ -1979,7 +1983,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, return nullptr; } -ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, +ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, ProgramStateRef State) { if (!State) return nullptr; @@ -1992,7 +1996,7 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, SVal count = State->getSVal(CE->getArg(0), LCtx); SVal elementSize = State->getSVal(CE->getArg(1), LCtx); SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize, - svalBuilder.getContext().getSizeType()); + svalBuilder.getContext().getSizeType()); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); return MallocMemAux(C, CE, TotalSize, zeroVal, State); @@ -2079,7 +2083,7 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, const ExplodedNode *AllocNode = nullptr; const MemRegion *Region = nullptr; std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C); - + ProgramPoint P = AllocNode->getLocation(); const Stmt *AllocationStmt = nullptr; if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) @@ -2128,7 +2132,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, } } - + // Cleanup the Realloc Pairs Map. ReallocPairsTy RP = state->get<ReallocPairs>(); for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { @@ -2151,10 +2155,12 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, ExplodedNode *N = C.getPredecessor(); if (!Errors.empty()) { static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak"); - N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); - for (SmallVectorImpl<SymbolRef>::iterator + N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (N) { + for (SmallVectorImpl<SymbolRef>::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { - reportLeak(*I, N, C); + reportLeak(*I, N, C); + } } } @@ -2233,7 +2239,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { } // TODO: Blocks should be either inlined or should call invalidate regions -// upon invocation. After that's in place, special casing here will not be +// upon invocation. After that's in place, special casing here will not be // needed. void MallocChecker::checkPostStmt(const BlockExpr *BE, CheckerContext &C) const { @@ -2292,10 +2298,14 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, const Stmt *S) const { assert(Sym); - const RefState *RS = C.getState()->get<RegionState>(Sym); - if (RS && RS->isAllocatedOfSizeZero()) - ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym); + if (const RefState *RS = C.getState()->get<RegionState>(Sym)) { + if (RS->isAllocatedOfSizeZero()) + ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym); + } + else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) { + ReportUseZeroAllocated(C, S->getSourceRange(), Sym); + } } bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const { @@ -2377,7 +2387,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { // If it's not a framework call, or if it takes a callback, assume it // can free memory. - if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg()) + if (!Call->isInSystemHeader() || Call->argumentsMayEscape()) return true; // If it's a method we know about, handle it explicitly post-call. @@ -2447,7 +2457,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( StringRef FName = II->getName(); // White list the 'XXXNoCopy' CoreFoundation functions. - // We specifically check these before + // We specifically check these before if (FName.endswith("NoCopy")) { // Look for the deallocator argument. We know that the memory ownership // is not transferred only if the deallocator argument is @@ -2556,7 +2566,7 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, if (EscapingSymbol && EscapingSymbol != sym) continue; - + if (const RefState *RS = State->get<RegionState>(sym)) { if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && CheckRefState(RS)) { @@ -2703,7 +2713,7 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = mgr.getCurrentCheckName(); - // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete + // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete // checker. if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index e913479..99ba90d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -23,19 +23,22 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; +using llvm::APInt; +using llvm::APSInt; namespace { struct MallocOverflowCheck { const BinaryOperator *mulop; const Expr *variable; + APSInt maxVal; - MallocOverflowCheck (const BinaryOperator *m, const Expr *v) - : mulop(m), variable (v) - {} + MallocOverflowCheck(const BinaryOperator *m, const Expr *v, APSInt val) + : mulop(m), variable(v), maxVal(val) {} }; class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> { @@ -54,6 +57,11 @@ public: }; } // end anonymous namespace +// Return true for computations which evaluate to zero: e.g., mult by 0. +static inline bool EvaluatesToZero(APSInt &Val, BinaryOperatorKind op) { + return (op == BO_Mul) && (Val == 0); +} + void MallocOverflowSecurityChecker::CheckMallocArgument( SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, const Expr *TheArgument, @@ -64,13 +72,14 @@ void MallocOverflowSecurityChecker::CheckMallocArgument( Reject anything that applies to the variable: an explicit cast, conditional expression, an operation that could reduce the range of the result, or anything too complicated :-). */ - const Expr * e = TheArgument; + const Expr *e = TheArgument; const BinaryOperator * mulop = nullptr; + APSInt maxVal; for (;;) { + maxVal = 0; e = e->IgnoreParenImpCasts(); - if (isa<BinaryOperator>(e)) { - const BinaryOperator * binop = dyn_cast<BinaryOperator>(e); + if (const BinaryOperator *binop = dyn_cast<BinaryOperator>(e)) { BinaryOperatorKind opc = binop->getOpcode(); // TODO: ignore multiplications by 1, reject if multiplied by 0. if (mulop == nullptr && opc == BO_Mul) @@ -80,12 +89,18 @@ void MallocOverflowSecurityChecker::CheckMallocArgument( const Expr *lhs = binop->getLHS(); const Expr *rhs = binop->getRHS(); - if (rhs->isEvaluatable(Context)) + if (rhs->isEvaluatable(Context)) { e = lhs; - else if ((opc == BO_Add || opc == BO_Mul) - && lhs->isEvaluatable(Context)) + maxVal = rhs->EvaluateKnownConstInt(Context); + if (EvaluatesToZero(maxVal, opc)) + return; + } else if ((opc == BO_Add || opc == BO_Mul) && + lhs->isEvaluatable(Context)) { + maxVal = lhs->EvaluateKnownConstInt(Context); + if (EvaluatesToZero(maxVal, opc)) + return; e = rhs; - else + } else return; } else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e)) @@ -103,7 +118,7 @@ void MallocOverflowSecurityChecker::CheckMallocArgument( // TODO: Could push this into the innermost scope where 'e' is // defined, rather than the whole function. - PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e)); + PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e, maxVal)); } namespace { @@ -126,33 +141,84 @@ private: return false; } - void CheckExpr(const Expr *E_p) { - const Expr *E = E_p->IgnoreParenImpCasts(); + const Decl *getDecl(const DeclRefExpr *DR) { return DR->getDecl(); } + + const Decl *getDecl(const MemberExpr *ME) { return ME->getMemberDecl(); } + template <typename T1> + void Erase(const T1 *DR, std::function<bool(theVecType::iterator)> pred) { theVecType::iterator i = toScanFor.end(); theVecType::iterator e = toScanFor.begin(); - - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) { - const Decl * EdreD = DR->getDecl(); - while (i != e) { - --i; - if (const DeclRefExpr *DR_i = dyn_cast<DeclRefExpr>(i->variable)) { - if (DR_i->getDecl() == EdreD) - i = toScanFor.erase(i); - } + while (i != e) { + --i; + if (const T1 *DR_i = dyn_cast<T1>(i->variable)) { + if ((getDecl(DR_i) == getDecl(DR)) && pred(i)) + i = toScanFor.erase(i); } } + } + + void CheckExpr(const Expr *E_p) { + auto PredTrue = [](theVecType::iterator) -> bool { return true; }; + const Expr *E = E_p->IgnoreParenImpCasts(); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + Erase<DeclRefExpr>(DR, PredTrue); else if (const auto *ME = dyn_cast<MemberExpr>(E)) { - // No points-to analysis, just look at the member - const Decl *EmeMD = ME->getMemberDecl(); - while (i != e) { - --i; - if (const auto *ME_i = dyn_cast<MemberExpr>(i->variable)) { - if (ME_i->getMemberDecl() == EmeMD) - i = toScanFor.erase (i); - } + Erase<MemberExpr>(ME, PredTrue); + } + } + + // Check if the argument to malloc is assigned a value + // which cannot cause an overflow. + // e.g., malloc (mul * x) and, + // case 1: mul = <constant value> + // case 2: mul = a/b, where b > x + void CheckAssignmentExpr(BinaryOperator *AssignEx) { + bool assignKnown = false; + bool numeratorKnown = false, denomKnown = false; + APSInt denomVal; + denomVal = 0; + + // Erase if the multiplicand was assigned a constant value. + const Expr *rhs = AssignEx->getRHS(); + if (rhs->isEvaluatable(Context)) + assignKnown = true; + + // Discard the report if the multiplicand was assigned a value, + // that can never overflow after multiplication. e.g., the assignment + // is a division operator and the denominator is > other multiplicand. + const Expr *rhse = rhs->IgnoreParenImpCasts(); + if (const BinaryOperator *BOp = dyn_cast<BinaryOperator>(rhse)) { + if (BOp->getOpcode() == BO_Div) { + const Expr *denom = BOp->getRHS()->IgnoreParenImpCasts(); + if (denom->EvaluateAsInt(denomVal, Context)) + denomKnown = true; + const Expr *numerator = BOp->getLHS()->IgnoreParenImpCasts(); + if (numerator->isEvaluatable(Context)) + numeratorKnown = true; } } + if (!assignKnown && !denomKnown) + return; + auto denomExtVal = denomVal.getExtValue(); + + // Ignore negative denominator. + if (denomExtVal < 0) + return; + + const Expr *lhs = AssignEx->getLHS(); + const Expr *E = lhs->IgnoreParenImpCasts(); + + auto pred = [assignKnown, numeratorKnown, + denomExtVal](theVecType::iterator i) { + return assignKnown || + (numeratorKnown && (denomExtVal >= i->maxVal.getExtValue())); + }; + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + Erase<DeclRefExpr>(DR, pred); + else if (const auto *ME = dyn_cast<MemberExpr>(E)) + Erase<MemberExpr>(ME, pred); } public: @@ -162,11 +228,13 @@ private: const Expr * rhs = E->getRHS(); // Ignore comparisons against zero, since they generally don't // protect against an overflow. - if (!isIntZeroExpr(lhs) && ! isIntZeroExpr(rhs)) { + if (!isIntZeroExpr(lhs) && !isIntZeroExpr(rhs)) { CheckExpr(lhs); CheckExpr(rhs); } } + if (E->isAssignmentOp()) + CheckAssignmentExpr(E); EvaluatedExprVisitor<CheckOverflowOps>::VisitBinaryOperator(E); } @@ -243,12 +311,12 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, const FunctionDecl *FD = TheCall->getDirectCallee(); if (!FD) - return; + continue; // Get the name of the callee. If it's a builtin, strip off the prefix. IdentifierInfo *FnInfo = FD->getIdentifier(); if (!FnInfo) - return; + continue; if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { if (TheCall->getNumArgs() == 1) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index fb07484..80a3fbe 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -143,20 +143,20 @@ static bool typesCompatible(ASTContext &C, QualType A, QualType B) { while (true) { A = A.getCanonicalType(); B = B.getCanonicalType(); - + if (A.getTypePtr() == B.getTypePtr()) return true; - + if (const PointerType *ptrA = A->getAs<PointerType>()) if (const PointerType *ptrB = B->getAs<PointerType>()) { A = ptrA->getPointeeType(); B = ptrB->getPointeeType(); continue; } - + break; } - + return false; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index d23708e..0e78947 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -11,7 +11,7 @@ // about subpar uses of NSAutoreleasePool. Note that while the check itself // (in its 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. +// It is also a good example of the CheckerVisitor interface. // //===----------------------------------------------------------------------===// @@ -48,7 +48,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); if (!OD) - return; + return; if (!OD->getIdentifier()->isStr("NSAutoreleasePool")) return; @@ -62,7 +62,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, BT.reset(new BugType(this, "Use -drain instead of -release", "API Upgrade (Apple)")); - ExplodedNode *N = C.addTransition(); + ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) { assert(0); return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index c351c6e..dab068b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -58,7 +58,7 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, return; if (!II) - II = &D->getASTContext().Idents.get("NSError"); + II = &D->getASTContext().Idents.get("NSError"); bool hasNSError = false; for (const auto *I : D->params()) { @@ -105,7 +105,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, return; if (!II) - II = &D->getASTContext().Idents.get("CFErrorRef"); + II = &D->getASTContext().Idents.get("CFErrorRef"); bool hasCFError = false; for (auto I : D->params()) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index ba82d1d..c1deade 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -66,6 +66,7 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, .Case("assfail", true) .Case("db_error", true) .Case("__assert", true) + .Case("__assert2", true) // For the purpose of static analysis, we do not care that // this MSVC function will return if the user decides to continue. .Case("_wassert", true) @@ -81,7 +82,7 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, } if (BuildSinks) - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); } void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, @@ -90,7 +91,7 @@ void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, if (const ObjCMethodDecl *MD = Msg.getDecl()) { MD = MD->getCanonicalDecl(); if (MD->hasAttr<AnalyzerNoReturnAttr>()) { - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); return; } } @@ -136,7 +137,7 @@ void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, } // If we got here, it's one of the messages we care about. - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); } void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 73f8087..1f82ab9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -28,7 +28,7 @@ using namespace ento; namespace { class NonNullParamChecker - : public Checker< check::PreCall > { + : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { mutable std::unique_ptr<BugType> BTAttrNonNull; mutable std::unique_ptr<BugType> BTNullRefArg; @@ -139,26 +139,34 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, ProgramStateRef stateNotNull, stateNull; std::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)) { + if (stateNull) { + if (!stateNotNull) { + // Generate an error node. Check for a null node in case + // we cache out. + if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { - std::unique_ptr<BugReport> R; - if (haveAttrNonNull) - R = genReportNullAttrNonNull(errorNode, ArgE); - else if (haveRefTypeParam) - R = genReportReferenceToNullPointer(errorNode, ArgE); + std::unique_ptr<BugReport> R; + if (haveAttrNonNull) + R = genReportNullAttrNonNull(errorNode, ArgE); + else if (haveRefTypeParam) + R = genReportReferenceToNullPointer(errorNode, ArgE); - // Highlight the range of the argument that was null. - R->addRange(Call.getArgSourceRange(idx)); + // Highlight the range of the argument that was null. + R->addRange(Call.getArgSourceRange(idx)); - // Emit the bug report. - C.emitReport(std::move(R)); - } + // Emit the bug report. + C.emitReport(std::move(R)); + } - // Always return. Either we cached out or we just emitted an error. - return; + // Always return. Either we cached out or we just emitted an error. + return; + } + if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { + ImplicitNullDerefEvent event = { + V, false, N, &C.getBugReporter(), + /*IsDirectDereference=*/haveRefTypeParam}; + dispatchEvent(event); + } } // If a pointer value passed the check we should assume that it is diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp new file mode 100644 index 0000000..bb86ea4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -0,0 +1,1066 @@ +//== Nullabilityhecker.cpp - Nullability checker ----------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker tries to find nullability violations. There are several kinds of +// possible violations: +// * Null pointer is passed to a pointer which has a _Nonnull type. +// * Null pointer is returned from a function which has a _Nonnull return type. +// * Nullable pointer is passed to a pointer which has a _Nonnull type. +// * Nullable pointer is returned from a function which has a _Nonnull return +// type. +// * Nullable pointer is dereferenced. +// +// This checker propagates the nullability information of the pointers and looks +// for the patterns that are described above. Explicit casts are trusted and are +// considered a way to suppress false positives for this checker. The other way +// to suppress warnings would be to add asserts or guarding if statements to the +// code. In addition to the nullability propagation this checker also uses some +// heuristics to suppress potential false positives. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "llvm/Support/Path.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +using namespace clang; +using namespace ento; + +namespace { +// Do not reorder! The getMostNullable method relies on the order. +// Optimization: Most pointers expected to be unspecified. When a symbol has an +// unspecified or nonnull type non of the rules would indicate any problem for +// that symbol. For this reason only nullable and contradicted nullability are +// stored for a symbol. When a symbol is already contradicted, it can not be +// casted back to nullable. +enum class Nullability : char { + Contradicted, // Tracked nullability is contradicted by an explicit cast. Do + // not report any nullability related issue for this symbol. + // This nullability is propagated agressively to avoid false + // positive results. See the comment on getMostNullable method. + Nullable, + Unspecified, + Nonnull +}; + +/// Returns the most nullable nullability. This is used for message expressions +/// like [reciever method], where the nullability of this expression is either +/// the nullability of the receiver or the nullability of the return type of the +/// method, depending on which is more nullable. Contradicted is considered to +/// be the most nullable, to avoid false positive results. +Nullability getMostNullable(Nullability Lhs, Nullability Rhs) { + return static_cast<Nullability>( + std::min(static_cast<char>(Lhs), static_cast<char>(Rhs))); +} + +const char *getNullabilityString(Nullability Nullab) { + switch (Nullab) { + case Nullability::Contradicted: + return "contradicted"; + case Nullability::Nullable: + return "nullable"; + case Nullability::Unspecified: + return "unspecified"; + case Nullability::Nonnull: + return "nonnull"; + } + llvm_unreachable("Unexpected enumeration."); + return ""; +} + +// These enums are used as an index to ErrorMessages array. +enum class ErrorKind : int { + NilAssignedToNonnull, + NilPassedToNonnull, + NilReturnedToNonnull, + NullableAssignedToNonnull, + NullableReturnedToNonnull, + NullableDereferenced, + NullablePassedToNonnull +}; + +const char *const ErrorMessages[] = { + "Null is assigned to a pointer which is expected to have non-null value", + "Null passed to a callee that requires a non-null argument", + "Null is returned from a function that is expected to return a non-null " + "value", + "Nullable pointer is assigned to a pointer which is expected to have " + "non-null value", + "Nullable pointer is returned from a function that is expected to return a " + "non-null value", + "Nullable pointer is dereferenced", + "Nullable pointer is passed to a callee that requires a non-null argument"}; + +class NullabilityChecker + : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, + check::PostCall, check::PostStmt<ExplicitCastExpr>, + check::PostObjCMessage, check::DeadSymbols, + check::Event<ImplicitNullDerefEvent>> { + mutable std::unique_ptr<BugType> BT; + +public: + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + void checkEvent(ImplicitNullDerefEvent Event) const; + + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; + + struct NullabilityChecksFilter { + DefaultBool CheckNullPassedToNonnull; + DefaultBool CheckNullReturnedFromNonnull; + DefaultBool CheckNullableDereferenced; + DefaultBool CheckNullablePassedToNonnull; + DefaultBool CheckNullableReturnedFromNonnull; + + CheckName CheckNameNullPassedToNonnull; + CheckName CheckNameNullReturnedFromNonnull; + CheckName CheckNameNullableDereferenced; + CheckName CheckNameNullablePassedToNonnull; + CheckName CheckNameNullableReturnedFromNonnull; + }; + + NullabilityChecksFilter Filter; + // When set to false no nullability information will be tracked in + // NullabilityMap. It is possible to catch errors like passing a null pointer + // to a callee that expects nonnull argument without the information that is + // stroed in the NullabilityMap. This is an optimization. + DefaultBool NeedTracking; + +private: + class NullabilityBugVisitor + : public BugReporterVisitorImpl<NullabilityBugVisitor> { + public: + NullabilityBugVisitor(const MemRegion *M) : Region(M) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Region); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked region. + const MemRegion *Region; + }; + + /// When any of the nonnull arguments of the analyzed function is null, do not + /// report anything and turn off the check. + /// + /// When \p SuppressPath is set to true, no more bugs will be reported on this + /// path by this checker. + void reportBugIfPreconditionHolds(ErrorKind Error, ExplodedNode *N, + const MemRegion *Region, CheckerContext &C, + const Stmt *ValueExpr = nullptr, + bool SuppressPath = false) const; + + void reportBug(ErrorKind Error, ExplodedNode *N, const MemRegion *Region, + BugReporter &BR, const Stmt *ValueExpr = nullptr) const { + if (!BT) + BT.reset(new BugType(this, "Nullability", "Memory error")); + const char *Msg = ErrorMessages[static_cast<int>(Error)]; + std::unique_ptr<BugReport> R(new BugReport(*BT, Msg, N)); + if (Region) { + R->markInteresting(Region); + R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region)); + } + if (ValueExpr) { + R->addRange(ValueExpr->getSourceRange()); + if (Error == ErrorKind::NilAssignedToNonnull || + Error == ErrorKind::NilPassedToNonnull || + Error == ErrorKind::NilReturnedToNonnull) + bugreporter::trackNullOrUndefValue(N, ValueExpr, *R); + } + BR.emitReport(std::move(R)); + } + + /// If an SVal wraps a region that should be tracked, it will return a pointer + /// to the wrapped region. Otherwise it will return a nullptr. + const SymbolicRegion *getTrackRegion(SVal Val, + bool CheckSuperRegion = false) const; +}; + +class NullabilityState { +public: + NullabilityState(Nullability Nullab, const Stmt *Source = nullptr) + : Nullab(Nullab), Source(Source) {} + + const Stmt *getNullabilitySource() const { return Source; } + + Nullability getValue() const { return Nullab; } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(static_cast<char>(Nullab)); + ID.AddPointer(Source); + } + + void print(raw_ostream &Out) const { + Out << getNullabilityString(Nullab) << "\n"; + } + +private: + Nullability Nullab; + // Source is the expression which determined the nullability. For example in a + // message like [nullable nonnull_returning] has nullable nullability, because + // the receiver is nullable. Here the receiver will be the source of the + // nullability. This is useful information when the diagnostics are generated. + const Stmt *Source; +}; + +bool operator==(NullabilityState Lhs, NullabilityState Rhs) { + return Lhs.getValue() == Rhs.getValue() && + Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); +} + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, + NullabilityState) + +// If the nullability precondition of a function is violated, we should not +// report nullability related issues on that path. For this reason once a +// precondition is not met on a path, this checker will be esentially turned off +// for the rest of the analysis. We do not want to generate a sink node however, +// so this checker would not lead to reduced coverage. +REGISTER_TRAIT_WITH_PROGRAMSTATE(PreconditionViolated, bool) + +enum class NullConstraint { IsNull, IsNotNull, Unknown }; + +static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, + ProgramStateRef State) { + ConditionTruthVal Nullness = State->isNull(Val); + if (Nullness.isConstrainedFalse()) + return NullConstraint::IsNotNull; + if (Nullness.isConstrainedTrue()) + return NullConstraint::IsNull; + return NullConstraint::Unknown; +} + +const SymbolicRegion * +NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { + if (!NeedTracking) + return nullptr; + + auto RegionSVal = Val.getAs<loc::MemRegionVal>(); + if (!RegionSVal) + return nullptr; + + const MemRegion *Region = RegionSVal->getRegion(); + + if (CheckSuperRegion) { + if (auto FieldReg = Region->getAs<FieldRegion>()) + return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); + if (auto ElementReg = Region->getAs<ElementRegion>()) + return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); + } + + return dyn_cast<SymbolicRegion>(Region); +} + +PathDiagnosticPiece *NullabilityChecker::NullabilityBugVisitor::VisitNode( + const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, + BugReport &BR) { + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = PrevN->getState(); + + const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region); + const NullabilityState *TrackedNullabPrev = + StatePrev->get<NullabilityMap>(Region); + if (!TrackedNullab) + return nullptr; + + if (TrackedNullabPrev && + TrackedNullabPrev->getValue() == TrackedNullab->getValue()) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = TrackedNullab->getNullabilitySource(); + if (!S) { + ProgramPoint ProgLoc = N->getLocation(); + if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) { + S = SP->getStmt(); + } + } + + if (!S) + return nullptr; + + std::string InfoText = + (llvm::Twine("Nullability '") + + getNullabilityString(TrackedNullab->getValue()) + "' is infered") + .str(); + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(Pos, InfoText, true, nullptr); +} + +static Nullability getNullabilityAnnotation(QualType Type) { + const auto *AttrType = Type->getAs<AttributedType>(); + if (!AttrType) + return Nullability::Unspecified; + if (AttrType->getAttrKind() == AttributedType::attr_nullable) + return Nullability::Nullable; + else if (AttrType->getAttrKind() == AttributedType::attr_nonnull) + return Nullability::Nonnull; + return Nullability::Unspecified; +} + +template <typename ParamVarDeclRange> +static bool +checkParamsForPreconditionViolation(const ParamVarDeclRange &Params, + ProgramStateRef State, + const LocationContext *LocCtxt) { + for (const auto *ParamDecl : Params) { + if (ParamDecl->isParameterPack()) + break; + + if (getNullabilityAnnotation(ParamDecl->getType()) != Nullability::Nonnull) + continue; + + auto RegVal = State->getLValue(ParamDecl, LocCtxt) + .template getAs<loc::MemRegionVal>(); + if (!RegVal) + continue; + + auto ParamValue = State->getSVal(RegVal->getRegion()) + .template getAs<DefinedOrUnknownSVal>(); + if (!ParamValue) + continue; + + if (getNullConstraint(*ParamValue, State) == NullConstraint::IsNull) { + return true; + } + } + return false; +} + +static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N, + CheckerContext &C) { + if (State->get<PreconditionViolated>()) + return true; + + const LocationContext *LocCtxt = C.getLocationContext(); + const Decl *D = LocCtxt->getDecl(); + if (!D) + return false; + + if (const auto *BlockD = dyn_cast<BlockDecl>(D)) { + if (checkParamsForPreconditionViolation(BlockD->parameters(), State, + LocCtxt)) { + if (!N->isSink()) + C.addTransition(State->set<PreconditionViolated>(true), N); + return true; + } + return false; + } + + if (const auto *FuncDecl = dyn_cast<FunctionDecl>(D)) { + if (checkParamsForPreconditionViolation(FuncDecl->parameters(), State, + LocCtxt)) { + if (!N->isSink()) + C.addTransition(State->set<PreconditionViolated>(true), N); + return true; + } + return false; + } + return false; +} + +void NullabilityChecker::reportBugIfPreconditionHolds( + ErrorKind Error, ExplodedNode *N, const MemRegion *Region, + CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { + ProgramStateRef OriginalState = N->getState(); + + if (checkPreconditionViolation(OriginalState, N, C)) + return; + if (SuppressPath) { + OriginalState = OriginalState->set<PreconditionViolated>(true); + N = C.addTransition(OriginalState, N); + } + + reportBug(Error, N, Region, C.getBugReporter(), ValueExpr); +} + +/// Cleaning up the program state. +void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + if (!SR.hasDeadSymbols()) + return; + + ProgramStateRef State = C.getState(); + NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); + for (NullabilityMapTy::iterator I = Nullabilities.begin(), + E = Nullabilities.end(); + I != E; ++I) { + const auto *Region = I->first->getAs<SymbolicRegion>(); + assert(Region && "Non-symbolic region is tracked."); + if (SR.isDead(Region->getSymbol())) { + State = State->remove<NullabilityMap>(I->first); + } + } + // When one of the nonnull arguments are constrained to be null, nullability + // preconditions are violated. It is not enough to check this only when we + // actually report an error, because at that time interesting symbols might be + // reaped. + if (checkPreconditionViolation(State, C.getPredecessor(), C)) + return; + C.addTransition(State); +} + +/// This callback triggers when a pointer is dereferenced and the analyzer does +/// not know anything about the value of that pointer. When that pointer is +/// nullable, this code emits a warning. +void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { + if (Event.SinkNode->getState()->get<PreconditionViolated>()) + return; + + const MemRegion *Region = + getTrackRegion(Event.Location, /*CheckSuperregion=*/true); + if (!Region) + return; + + ProgramStateRef State = Event.SinkNode->getState(); + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (!TrackedNullability) + return; + + if (Filter.CheckNullableDereferenced && + TrackedNullability->getValue() == Nullability::Nullable) { + BugReporter &BR = *Event.BR; + // Do not suppress errors on defensive code paths, because dereferencing + // a nullable pointer is always an error. + if (Event.IsDirectDereference) + reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); + else + reportBug(ErrorKind::NullablePassedToNonnull, Event.SinkNode, Region, BR); + } +} + +/// This method check when nullable pointer or null value is returned from a +/// function that has nonnull return type. +/// +/// TODO: when nullability preconditons are violated, it is ok to violate the +/// nullability postconditons (i.e.: when one of the nonnull parameters are null +/// this check should not report any nullability related issue). +void NullabilityChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { + auto RetExpr = S->getRetValue(); + if (!RetExpr) + return; + + if (!RetExpr->getType()->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<PreconditionViolated>()) + return; + + auto RetSVal = + State->getSVal(S, C.getLocationContext()).getAs<DefinedOrUnknownSVal>(); + if (!RetSVal) + return; + + AnalysisDeclContext *DeclCtxt = + C.getLocationContext()->getAnalysisDeclContext(); + const FunctionType *FuncType = DeclCtxt->getDecl()->getFunctionType(); + if (!FuncType) + return; + + NullConstraint Nullness = getNullConstraint(*RetSVal, State); + + Nullability RequiredNullability = + getNullabilityAnnotation(FuncType->getReturnType()); + + // If the returned value is null but the type of the expression + // generating it is nonnull then we will suppress the diagnostic. + // This enables explicit suppression when returning a nil literal in a + // function with a _Nonnull return type: + // return (NSString * _Nonnull)0; + Nullability RetExprTypeLevelNullability = + getNullabilityAnnotation(RetExpr->getType()); + + if (Filter.CheckNullReturnedFromNonnull && + Nullness == NullConstraint::IsNull && + RetExprTypeLevelNullability != Nullability::Nonnull && + RequiredNullability == Nullability::Nonnull) { + static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); + ExplodedNode *N = C.generateErrorNode(State, &Tag); + if (!N) + return; + reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N, nullptr, C, + RetExpr); + return; + } + + const MemRegion *Region = getTrackRegion(*RetSVal); + if (!Region) + return; + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + if (TrackedNullability) { + Nullability TrackedNullabValue = TrackedNullability->getValue(); + if (Filter.CheckNullableReturnedFromNonnull && + Nullness != NullConstraint::IsNotNull && + TrackedNullabValue == Nullability::Nullable && + RequiredNullability == Nullability::Nonnull) { + static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); + ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); + reportBugIfPreconditionHolds(ErrorKind::NullableReturnedToNonnull, N, + Region, C); + } + return; + } + if (RequiredNullability == Nullability::Nullable) { + State = State->set<NullabilityMap>(Region, + NullabilityState(RequiredNullability, + S)); + C.addTransition(State); + } +} + +/// This callback warns when a nullable pointer or a null value is passed to a +/// function that expects its argument to be nonnull. +void NullabilityChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.getDecl()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<PreconditionViolated>()) + return; + + ProgramStateRef OrigState = State; + + unsigned Idx = 0; + for (const ParmVarDecl *Param : Call.parameters()) { + if (Param->isParameterPack()) + break; + + const Expr *ArgExpr = nullptr; + if (Idx < Call.getNumArgs()) + ArgExpr = Call.getArgExpr(Idx); + auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); + if (!ArgSVal) + continue; + + if (!Param->getType()->isAnyPointerType() && + !Param->getType()->isReferenceType()) + continue; + + NullConstraint Nullness = getNullConstraint(*ArgSVal, State); + + Nullability RequiredNullability = + getNullabilityAnnotation(Param->getType()); + Nullability ArgExprTypeLevelNullability = + getNullabilityAnnotation(ArgExpr->getType()); + + if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && + ArgExprTypeLevelNullability != Nullability::Nonnull && + RequiredNullability == Nullability::Nonnull) { + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; + reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C, + ArgExpr); + return; + } + + const MemRegion *Region = getTrackRegion(*ArgSVal); + if (!Region) + continue; + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (TrackedNullability) { + if (Nullness == NullConstraint::IsNotNull || + TrackedNullability->getValue() != Nullability::Nullable) + continue; + + if (Filter.CheckNullablePassedToNonnull && + RequiredNullability == Nullability::Nonnull) { + ExplodedNode *N = C.addTransition(State); + reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N, + Region, C, ArgExpr, /*SuppressPath=*/true); + return; + } + if (Filter.CheckNullableDereferenced && + Param->getType()->isReferenceType()) { + ExplodedNode *N = C.addTransition(State); + reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region, + C, ArgExpr, /*SuppressPath=*/true); + return; + } + continue; + } + // No tracked nullability yet. + if (ArgExprTypeLevelNullability != Nullability::Nullable) + continue; + State = State->set<NullabilityMap>( + Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr)); + } + if (State != OrigState) + C.addTransition(State); +} + +/// Suppress the nullability warnings for some functions. +void NullabilityChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + auto Decl = Call.getDecl(); + if (!Decl) + return; + // ObjC Messages handles in a different callback. + if (Call.getKind() == CE_ObjCMessage) + return; + const FunctionType *FuncType = Decl->getFunctionType(); + if (!FuncType) + return; + QualType ReturnType = FuncType->getReturnType(); + if (!ReturnType->isAnyPointerType()) + return; + ProgramStateRef State = C.getState(); + if (State->get<PreconditionViolated>()) + return; + + const MemRegion *Region = getTrackRegion(Call.getReturnValue()); + if (!Region) + return; + + // CG headers are misannotated. Do not warn for symbols that are the results + // of CG calls. + const SourceManager &SM = C.getSourceManager(); + StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getLocStart())); + if (llvm::sys::path::filename(FilePath).startswith("CG")) { + State = State->set<NullabilityMap>(Region, Nullability::Contradicted); + C.addTransition(State); + return; + } + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (!TrackedNullability && + getNullabilityAnnotation(ReturnType) == Nullability::Nullable) { + State = State->set<NullabilityMap>(Region, Nullability::Nullable); + C.addTransition(State); + } +} + +static Nullability getReceiverNullability(const ObjCMethodCall &M, + ProgramStateRef State) { + if (M.isReceiverSelfOrSuper()) { + // For super and super class receivers we assume that the receiver is + // nonnull. + return Nullability::Nonnull; + } + // Otherwise look up nullability in the state. + SVal Receiver = M.getReceiverSVal(); + if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) { + // If the receiver is constrained to be nonnull, assume that it is nonnull + // regardless of its type. + NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State); + if (Nullness == NullConstraint::IsNotNull) + return Nullability::Nonnull; + } + auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>(); + if (ValueRegionSVal) { + const MemRegion *SelfRegion = ValueRegionSVal->getRegion(); + assert(SelfRegion); + + const NullabilityState *TrackedSelfNullability = + State->get<NullabilityMap>(SelfRegion); + if (TrackedSelfNullability) + return TrackedSelfNullability->getValue(); + } + return Nullability::Unspecified; +} + +/// Calculate the nullability of the result of a message expr based on the +/// nullability of the receiver, the nullability of the return value, and the +/// constraints. +void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + auto Decl = M.getDecl(); + if (!Decl) + return; + QualType RetType = Decl->getReturnType(); + if (!RetType->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<PreconditionViolated>()) + return; + + const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue()); + if (!ReturnRegion) + return; + + auto Interface = Decl->getClassInterface(); + auto Name = Interface ? Interface->getName() : ""; + // In order to reduce the noise in the diagnostics generated by this checker, + // some framework and programming style based heuristics are used. These + // heuristics are for Cocoa APIs which have NS prefix. + if (Name.startswith("NS")) { + // Developers rely on dynamic invariants such as an item should be available + // in a collection, or a collection is not empty often. Those invariants can + // not be inferred by any static analysis tool. To not to bother the users + // with too many false positives, every item retrieval function should be + // ignored for collections. The instance methods of dictionaries in Cocoa + // are either item retrieval related or not interesting nullability wise. + // Using this fact, to keep the code easier to read just ignore the return + // value of every instance method of dictionaries. + if (M.isInstanceMessage() && Name.find("Dictionary") != StringRef::npos) { + State = + State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); + C.addTransition(State); + return; + } + // For similar reasons ignore some methods of Cocoa arrays. + StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0); + if (Name.find("Array") != StringRef::npos && + (FirstSelectorSlot == "firstObject" || + FirstSelectorSlot == "lastObject")) { + State = + State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); + C.addTransition(State); + return; + } + + // Encoding related methods of string should not fail when lossless + // encodings are used. Using lossless encodings is so frequent that ignoring + // this class of methods reduced the emitted diagnostics by about 30% on + // some projects (and all of that was false positives). + if (Name.find("String") != StringRef::npos) { + for (auto Param : M.parameters()) { + if (Param->getName() == "encoding") { + State = State->set<NullabilityMap>(ReturnRegion, + Nullability::Contradicted); + C.addTransition(State); + return; + } + } + } + } + + const ObjCMessageExpr *Message = M.getOriginExpr(); + Nullability SelfNullability = getReceiverNullability(M, State); + + const NullabilityState *NullabilityOfReturn = + State->get<NullabilityMap>(ReturnRegion); + + if (NullabilityOfReturn) { + // When we have a nullability tracked for the return value, the nullability + // of the expression will be the most nullable of the receiver and the + // return value. + Nullability RetValTracked = NullabilityOfReturn->getValue(); + Nullability ComputedNullab = + getMostNullable(RetValTracked, SelfNullability); + if (ComputedNullab != RetValTracked && + ComputedNullab != Nullability::Unspecified) { + const Stmt *NullabilitySource = + ComputedNullab == RetValTracked + ? NullabilityOfReturn->getNullabilitySource() + : Message->getInstanceReceiver(); + State = State->set<NullabilityMap>( + ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); + C.addTransition(State); + } + return; + } + + // No tracked information. Use static type information for return value. + Nullability RetNullability = getNullabilityAnnotation(RetType); + + // Properties might be computed. For this reason the static analyzer creates a + // new symbol each time an unknown property is read. To avoid false pozitives + // do not treat unknown properties as nullable, even when they explicitly + // marked nullable. + if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) + RetNullability = Nullability::Nonnull; + + Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); + if (ComputedNullab == Nullability::Nullable) { + const Stmt *NullabilitySource = ComputedNullab == RetNullability + ? Message + : Message->getInstanceReceiver(); + State = State->set<NullabilityMap>( + ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); + C.addTransition(State); + } +} + +/// Explicit casts are trusted. If there is a disagreement in the nullability +/// annotations in the destination and the source or '0' is casted to nonnull +/// track the value as having contraditory nullability. This will allow users to +/// suppress warnings. +void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, + CheckerContext &C) const { + QualType OriginType = CE->getSubExpr()->getType(); + QualType DestType = CE->getType(); + if (!OriginType->isAnyPointerType()) + return; + if (!DestType->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<PreconditionViolated>()) + return; + + Nullability DestNullability = getNullabilityAnnotation(DestType); + + // No explicit nullability in the destination type, so this cast does not + // change the nullability. + if (DestNullability == Nullability::Unspecified) + return; + + auto RegionSVal = + State->getSVal(CE, C.getLocationContext()).getAs<DefinedOrUnknownSVal>(); + const MemRegion *Region = getTrackRegion(*RegionSVal); + if (!Region) + return; + + // When 0 is converted to nonnull mark it as contradicted. + if (DestNullability == Nullability::Nonnull) { + NullConstraint Nullness = getNullConstraint(*RegionSVal, State); + if (Nullness == NullConstraint::IsNull) { + State = State->set<NullabilityMap>(Region, Nullability::Contradicted); + C.addTransition(State); + return; + } + } + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (!TrackedNullability) { + if (DestNullability != Nullability::Nullable) + return; + State = State->set<NullabilityMap>(Region, + NullabilityState(DestNullability, CE)); + C.addTransition(State); + return; + } + + if (TrackedNullability->getValue() != DestNullability && + TrackedNullability->getValue() != Nullability::Contradicted) { + State = State->set<NullabilityMap>(Region, Nullability::Contradicted); + C.addTransition(State); + } +} + +/// For a given statement performing a bind, attempt to syntactically +/// match the expression resulting in the bound value. +static const Expr * matchValueExprForBind(const Stmt *S) { + // For `x = e` the value expression is the right-hand side. + if (auto *BinOp = dyn_cast<BinaryOperator>(S)) { + if (BinOp->getOpcode() == BO_Assign) + return BinOp->getRHS(); + } + + // For `int x = e` the value expression is the initializer. + if (auto *DS = dyn_cast<DeclStmt>(S)) { + if (DS->isSingleDecl()) { + auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return nullptr; + + if (const Expr *Init = VD->getInit()) + return Init; + } + } + + return nullptr; +} + +/// Returns true if \param S is a DeclStmt for a local variable that +/// ObjC automated reference counting initialized with zero. +static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S) { + // We suppress diagnostics for ARC zero-initialized _Nonnull locals. This + // prevents false positives when a _Nonnull local variable cannot be + // initialized with an initialization expression: + // NSString * _Nonnull s; // no-warning + // @autoreleasepool { + // s = ... + // } + // + // FIXME: We should treat implicitly zero-initialized _Nonnull locals as + // uninitialized in Sema's UninitializedValues analysis to warn when a use of + // the zero-initialized definition will unexpectedly yield nil. + + // Locals are only zero-initialized when automated reference counting + // is turned on. + if (!C.getASTContext().getLangOpts().ObjCAutoRefCount) + return false; + + auto *DS = dyn_cast<DeclStmt>(S); + if (!DS || !DS->isSingleDecl()) + return false; + + auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return false; + + // Sema only zero-initializes locals with ObjCLifetimes. + if(!VD->getType().getQualifiers().hasObjCLifetime()) + return false; + + const Expr *Init = VD->getInit(); + assert(Init && "ObjC local under ARC without initializer"); + + // Return false if the local is explicitly initialized (e.g., with '= nil'). + if (!isa<ImplicitValueInitExpr>(Init)) + return false; + + return true; +} + +/// Propagate the nullability information through binds and warn when nullable +/// pointer or null symbol is assigned to a pointer with a nonnull type. +void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + const TypedValueRegion *TVR = + dyn_cast_or_null<TypedValueRegion>(L.getAsRegion()); + if (!TVR) + return; + + QualType LocType = TVR->getValueType(); + if (!LocType->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<PreconditionViolated>()) + return; + + auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>(); + if (!ValDefOrUnknown) + return; + + NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State); + + Nullability ValNullability = Nullability::Unspecified; + if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol()) + ValNullability = getNullabilityAnnotation(Sym->getType()); + + Nullability LocNullability = getNullabilityAnnotation(LocType); + if (Filter.CheckNullPassedToNonnull && + RhsNullness == NullConstraint::IsNull && + ValNullability != Nullability::Nonnull && + LocNullability == Nullability::Nonnull && + !isARCNilInitializedLocal(C, S)) { + static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); + ExplodedNode *N = C.generateErrorNode(State, &Tag); + if (!N) + return; + + const Stmt *ValueExpr = matchValueExprForBind(S); + if (!ValueExpr) + ValueExpr = S; + + reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, + ValueExpr); + return; + } + // Intentionally missing case: '0' is bound to a reference. It is handled by + // the DereferenceChecker. + + const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown); + if (!ValueRegion) + return; + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(ValueRegion); + + if (TrackedNullability) { + if (RhsNullness == NullConstraint::IsNotNull || + TrackedNullability->getValue() != Nullability::Nullable) + return; + if (Filter.CheckNullablePassedToNonnull && + LocNullability == Nullability::Nonnull) { + static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); + ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); + reportBugIfPreconditionHolds(ErrorKind::NullableAssignedToNonnull, N, + ValueRegion, C); + } + return; + } + + const auto *BinOp = dyn_cast<BinaryOperator>(S); + + if (ValNullability == Nullability::Nullable) { + // Trust the static information of the value more than the static + // information on the location. + const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S; + State = State->set<NullabilityMap>( + ValueRegion, NullabilityState(ValNullability, NullabilitySource)); + C.addTransition(State); + return; + } + + if (LocNullability == Nullability::Nullable) { + const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S; + State = State->set<NullabilityMap>( + ValueRegion, NullabilityState(LocNullability, NullabilitySource)); + C.addTransition(State); + } +} + +void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + NullabilityMapTy B = State->get<NullabilityMap>(); + + if (B.isEmpty()) + return; + + Out << Sep << NL; + + for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + Out << I->first << " : "; + I->second.print(Out); + Out << NL; + } +} + +#define REGISTER_CHECKER(name, trackingRequired) \ + void ento::register##name##Checker(CheckerManager &mgr) { \ + NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \ + checker->Filter.Check##name = true; \ + checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + checker->NeedTracking = checker->NeedTracking || trackingRequired; \ + } + +// The checks are likely to be turned on by default and it is possible to do +// them without tracking any nullability related information. As an optimization +// no nullability information will be tracked when only these two checks are +// enables. +REGISTER_CHECKER(NullPassedToNonnull, false) +REGISTER_CHECKER(NullReturnedFromNonnull, false) + +REGISTER_CHECKER(NullableDereferenced, true) +REGISTER_CHECKER(NullablePassedToNonnull, true) +REGISTER_CHECKER(NullableReturnedFromNonnull, true) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index a7b92b4..cbaa5c2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -43,7 +43,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, // Uninitialized value used for the mutex? if (V.getAs<UndefinedVal>()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_undef) BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " "for @synchronized")); @@ -66,7 +66,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. - if (ExplodedNode *N = C.addTransition(nullState)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) { if (!BT_null) BT_null.reset(new BuiltinBug( this, "Nil value used as mutex for @synchronized() " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp index 224251b..b10ec84 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -66,9 +66,8 @@ class WalkAST : public StmtVisitor<WalkAST> { // The type must be an array/pointer type. // This could be a null constant, which is allowed. - if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) - return true; - return false; + return static_cast<bool>( + E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)); } public: diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index 53e1598..0203d79 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -133,13 +133,13 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, if (IdxVal.isUnknownOrUndef()) return; DefinedSVal Idx = IdxVal.castAs<DefinedSVal>(); - + // Now, check if 'Idx in [0, Size-1]'. const QualType T = IdxExpr->getType(); ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; initBugType(); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index 016cb14..32a1adb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -49,7 +49,7 @@ public: DoesCallSuper = true; // Recurse if we didn't find the super call yet. - return !DoesCallSuper; + return !DoesCallSuper; } bool DoesCallSuper; @@ -59,7 +59,7 @@ private: }; //===----------------------------------------------------------------------===// -// ObjCSuperCallChecker +// ObjCSuperCallChecker //===----------------------------------------------------------------------===// class ObjCSuperCallChecker : public Checker< @@ -88,7 +88,7 @@ private: /// \param[out] SuperclassName On return, the found superclass name. bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, StringRef &SuperclassName) const { - const ObjCInterfaceDecl *ID = D->getClassInterface(); + const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass(); for ( ; ID ; ID = ID->getSuperClass()) { SuperclassName = ID->getIdentifier()->getName(); @@ -202,7 +202,7 @@ void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, SmallString<320> Buf; llvm::raw_svector_ostream os(Buf); - os << "The '" << S.getAsString() + os << "The '" << S.getAsString() << "' instance method in " << SuperclassName.str() << " subclass '" << *D << "' is missing a [super " << S.getAsString() << "] call"; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 93b0553..ffa3a27 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -145,15 +145,15 @@ void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C, const char *errorStr) const { if (!E) return; - + if (!C.getState()->get<CalledInit>()) return; - + if (!isInvalidSelf(E, C)) return; - + // Generate an error node. - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -177,12 +177,12 @@ void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, if (isInitMessage(Msg)) { // Tag the return value as the result of an initializer. ProgramStateRef 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(), C.getLocationContext()); addSelfFlag(state, V, SelfFlag_InitRes, C); return; @@ -318,7 +318,7 @@ void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const { // Allow assignment of anything to self. Self is a local variable in the // initializer, so it is legal to assign anything to it, like results of - // static functions/method calls. After self is assigned something we cannot + // static functions/method calls. After self is assigned something we cannot // reason about, stop enforcing the rules. // (Only continue checking if the assigned value should be treated as self.) if ((isSelfVar(loc, C)) && @@ -404,15 +404,12 @@ static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { if (II == NSObjectII) break; } - if (!ID) - return false; - - return true; + return ID != nullptr; } /// \brief Returns true if the location is 'self'. static bool isSelfVar(SVal location, CheckerContext &C) { - AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); + AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); if (!analCtx->getSelfDecl()) return false; if (!location.getAs<loc::MemRegionVal>()) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp new file mode 100644 index 0000000..8ce3735 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -0,0 +1,314 @@ +//=======- PaddingChecker.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 checker that checks for padding that could be +// removed by re-ordering members. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include <numeric> + +using namespace clang; +using namespace ento; + +namespace { +class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> { +private: + mutable std::unique_ptr<BugType> PaddingBug; + mutable int64_t AllowedPad; + mutable BugReporter *BR; + +public: + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + AllowedPad = + MGR.getAnalyzerOptions().getOptionAsInteger("AllowedPad", 24, this); + assert(AllowedPad >= 0 && "AllowedPad option should be non-negative"); + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { + const PaddingChecker *Checker; + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {} + bool VisitRecordDecl(const RecordDecl *RD) { + Checker->visitRecord(RD); + return true; + } + bool VisitVarDecl(const VarDecl *VD) { + Checker->visitVariable(VD); + return true; + } + // TODO: Visit array new and mallocs for arrays. + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); + } + + /// \brief Look for records of overly padded types. If padding * + /// PadMultiplier exceeds AllowedPad, then generate a report. + /// PadMultiplier is used to share code with the array padding + /// checker. + void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const { + if (shouldSkipDecl(RD)) + return; + + auto &ASTContext = RD->getASTContext(); + const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD); + assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity())); + + CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL); + if (BaselinePad.isZero()) + return; + CharUnits OptimalPad = calculateOptimalPad(RD, ASTContext, RL); + + CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad); + if (DiffPad.getQuantity() <= AllowedPad) { + assert(!DiffPad.isNegative() && "DiffPad should not be negative"); + // There is not enough excess padding to trigger a warning. + return; + } + reportRecord(RD, BaselinePad, OptimalPad); + } + + /// \brief Look for arrays of overly padded types. If the padding of the + /// array type exceeds AllowedPad, then generate a report. + void visitVariable(const VarDecl *VD) const { + const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe(); + if (ArrTy == nullptr) + return; + uint64_t Elts = 0; + if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy)) + Elts = CArrTy->getSize().getZExtValue(); + if (Elts == 0) + return; + const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>(); + if (RT == nullptr) + return; + + // TODO: Recurse into the fields and base classes to see if any + // of those have excess padding. + visitRecord(RT->getDecl(), Elts); + } + + bool shouldSkipDecl(const RecordDecl *RD) const { + auto Location = RD->getLocation(); + // If the construct doesn't have a source file, then it's not something + // we want to diagnose. + if (!Location.isValid()) + return true; + SrcMgr::CharacteristicKind Kind = + BR->getSourceManager().getFileCharacteristic(Location); + // Throw out all records that come from system headers. + if (Kind != SrcMgr::C_User) + return true; + + // Not going to attempt to optimize unions. + if (RD->isUnion()) + return true; + // How do you reorder fields if you haven't got any? + if (RD->field_empty()) + return true; + if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + // Tail padding with base classes ends up being very complicated. + // We will skip objects with base classes for now. + if (CXXRD->getNumBases() != 0) + return true; + // Virtual bases are complicated, skipping those for now. + if (CXXRD->getNumVBases() != 0) + return true; + // Can't layout a template, so skip it. We do still layout the + // instantiations though. + if (CXXRD->getTypeForDecl()->isDependentType()) + return true; + if (CXXRD->getTypeForDecl()->isInstantiationDependentType()) + return true; + } + auto IsTrickyField = [](const FieldDecl *FD) -> bool { + // Bitfield layout is hard. + if (FD->isBitField()) + return true; + + // Variable length arrays are tricky too. + QualType Ty = FD->getType(); + if (Ty->isIncompleteArrayType()) + return true; + return false; + }; + + if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField)) + return true; + return false; + } + + static CharUnits calculateBaselinePad(const RecordDecl *RD, + const ASTContext &ASTContext, + const ASTRecordLayout &RL) { + CharUnits PaddingSum; + CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); + for (const auto &FD : RD->fields()) { + // This checker only cares about the padded size of the + // field, and not the data size. If the field is a record + // with tail padding, then we won't put that number in our + // total because reordering fields won't fix that problem. + CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType()); + auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex()); + CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits); + PaddingSum += (FieldOffset - Offset); + Offset = FieldOffset + FieldSize; + } + PaddingSum += RL.getSize() - Offset; + return PaddingSum; + } + + /// Optimal padding overview: + /// 1. Find a close approximation to where we can place our first field. + /// This will usually be at offset 0. + /// 2. Try to find the best field that can legally be placed at the current + /// offset. + /// a. "Best" is the largest alignment that is legal, but smallest size. + /// This is to account for overly aligned types. + /// 3. If no fields can fit, pad by rounding the current offset up to the + /// smallest alignment requirement of our fields. Measure and track the + // amount of padding added. Go back to 2. + /// 4. Increment the current offset by the size of the chosen field. + /// 5. Remove the chosen field from the set of future possibilities. + /// 6. Go back to 2 if there are still unplaced fields. + /// 7. Add tail padding by rounding the current offset up to the structure + /// alignment. Track the amount of padding added. + + static CharUnits calculateOptimalPad(const RecordDecl *RD, + const ASTContext &ASTContext, + const ASTRecordLayout &RL) { + struct CharUnitPair { + CharUnits Align; + CharUnits Size; + bool operator<(const CharUnitPair &RHS) const { + // Order from small alignments to large alignments, + // then large sizes to small sizes. + return std::make_pair(Align, -Size) < + std::make_pair(RHS.Align, -RHS.Size); + } + }; + SmallVector<CharUnitPair, 20> Fields; + auto GatherSizesAndAlignments = [](const FieldDecl *FD) { + CharUnitPair RetVal; + auto &Ctx = FD->getASTContext(); + std::tie(RetVal.Size, RetVal.Align) = + Ctx.getTypeInfoInChars(FD->getType()); + assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity())); + if (auto Max = FD->getMaxAlignment()) + RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align); + return RetVal; + }; + std::transform(RD->field_begin(), RD->field_end(), + std::back_inserter(Fields), GatherSizesAndAlignments); + std::sort(Fields.begin(), Fields.end()); + + // This lets us skip over vptrs and non-virtual bases, + // so that we can just worry about the fields in our object. + // Note that this does cause us to miss some cases where we + // could pack more bytes in to a base class's tail padding. + CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); + CharUnits NewPad; + + while (!Fields.empty()) { + unsigned TrailingZeros = + llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity()); + // If NewOffset is zero, then countTrailingZeros will be 64. Shifting + // 64 will overflow our unsigned long long. Shifting 63 will turn + // our long long (and CharUnits internal type) negative. So shift 62. + long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u); + CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits); + CharUnitPair InsertPoint = {CurAlignment, CharUnits::Zero()}; + auto CurBegin = Fields.begin(); + auto CurEnd = Fields.end(); + + // In the typical case, this will find the last element + // of the vector. We won't find a middle element unless + // we started on a poorly aligned address or have an overly + // aligned field. + auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint); + if (Iter != CurBegin) { + // We found a field that we can layout with the current alignment. + --Iter; + NewOffset += Iter->Size; + Fields.erase(Iter); + } else { + // We are poorly aligned, and we need to pad in order to layout another + // field. Round up to at least the smallest field alignment that we + // currently have. + CharUnits NextOffset = NewOffset.RoundUpToAlignment(Fields[0].Align); + NewPad += NextOffset - NewOffset; + NewOffset = NextOffset; + } + } + // Calculate tail padding. + CharUnits NewSize = NewOffset.RoundUpToAlignment(RL.getAlignment()); + NewPad += NewSize - NewOffset; + return NewPad; + } + + void reportRecord(const RecordDecl *RD, CharUnits BaselinePad, + CharUnits TargetPad) const { + if (!PaddingBug) + PaddingBug = + llvm::make_unique<BugType>(this, "Excessive Padding", "Performance"); + + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + Os << "Excessive padding in '"; + Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers()) << "'"; + + if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { + // TODO: make this show up better in the console output and in + // the HTML. Maybe just make it show up in HTML like the path + // diagnostics show. + SourceLocation ILoc = TSD->getPointOfInstantiation(); + if (ILoc.isValid()) + Os << " instantiated here: " + << ILoc.printToString(BR->getSourceManager()); + } + + Os << " (" << BaselinePad.getQuantity() << " padding bytes, where " + << TargetPad.getQuantity() << " is optimal). Consider reordering " + << "the fields or adding explicit padding members."; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::create(RD, BR->getSourceManager()); + + auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc); + Report->setDeclWithIssue(RD); + Report->addRange(RD->getSourceRange()); + + BR->emitReport(std::move(Report)); + } +}; +} + +void ento::registerPaddingChecker(CheckerManager &Mgr) { + Mgr.registerChecker<PaddingChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 8063124..e336967 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -22,7 +22,7 @@ using namespace clang; using namespace ento; namespace { -class PointerArithChecker +class PointerArithChecker : public Checker< check::PreStmt<BinaryOperator> > { mutable std::unique_ptr<BuiltinBug> BT; @@ -48,10 +48,10 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B, // 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) || + if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || isa<CompoundLiteralRegion>(LR)) { - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Dangerous pointer arithmetic", diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index cf1f88a..2d33ebc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // // This files defines PointerSubChecker, a builtin checker that checks for -// pointer subtractions on two pointers pointing to different memory chunks. +// pointer subtractions on two pointers pointing to different memory chunks. // This check corresponds to CWE-469. // //===----------------------------------------------------------------------===// @@ -23,7 +23,7 @@ using namespace clang; using namespace ento; namespace { -class PointerSubChecker +class PointerSubChecker : public Checker< check::PreStmt<BinaryOperator> > { mutable std::unique_ptr<BuiltinBug> BT; @@ -60,7 +60,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) return; - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Pointer subtraction", diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 4209017..28a4a08 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -32,9 +32,9 @@ private: LockState(Kind K) : K(K) {} public: - static LockState getLocked(void) { return LockState(Locked); } - static LockState getUnlocked(void) { return LockState(Unlocked); } - static LockState getDestroyed(void) { return LockState(Destroyed); } + static LockState getLocked() { return LockState(Locked); } + static LockState getUnlocked() { return LockState(Unlocked); } + static LockState getDestroyed() { return LockState(Destroyed); } bool operator==(const LockState &X) const { return K == X.K; @@ -62,10 +62,10 @@ class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > { }; public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - + void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, bool isTryLock, enum LockingSemantics semantics) const; - + void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const; void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; @@ -96,7 +96,7 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE, false, PthreadSemantics); else if (FName == "lck_mtx_lock" || FName == "lck_rw_lock_exclusive" || - FName == "lck_rw_lock_shared") + FName == "lck_rw_lock_shared") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), false, XNUSemantics); else if (FName == "pthread_mutex_trylock" || @@ -124,17 +124,17 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE, void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, bool isTryLock, enum LockingSemantics semantics) const { - + const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; - + ProgramStateRef state = C.getState(); - + SVal X = state->getSVal(CE, C.getLocationContext()); if (X.isUnknownOrUndef()) return; - + DefinedSVal retVal = X.castAs<DefinedSVal>(); if (const LockState *LState = state->get<LockMap>(lockR)) { @@ -142,7 +142,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, if (!BT_doublelock) BT_doublelock.reset(new BugType(this, "Double locking", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto report = llvm::make_unique<BugReport>( @@ -183,8 +183,8 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, assert((semantics == XNUSemantics) && "Unknown locking semantics"); lockSucc = state; } - - // Record that the lock was acquired. + + // Record that the lock was acquired. lockSucc = lockSucc->add<LockSet>(lockR); lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); C.addTransition(lockSucc); @@ -196,7 +196,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; - + ProgramStateRef state = C.getState(); if (const LockState *LState = state->get<LockMap>(lockR)) { @@ -204,7 +204,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, if (!BT_doubleunlock) BT_doubleunlock.reset(new BugType(this, "Double unlocking", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique<BugReport>( @@ -227,7 +227,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, if (firstLockR != lockR) { if (!BT_lor) BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto report = llvm::make_unique<BugReport>( @@ -272,7 +272,7 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Destroy invalid lock", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); @@ -307,7 +307,7 @@ void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, if (!BT_initlock) BT_initlock.reset(new BugType(this, "Init invalid lock", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N); @@ -320,7 +320,7 @@ void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Use destroyed lock", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique<BugReport>( diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 6ee87a5..f983c30 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -234,6 +234,7 @@ public: return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(), getType(), IvarAccessHistory::AccessedDirectly); } + RefVal releaseViaIvar() const { assert(getIvarAccessHistory() == IvarAccessHistory::AccessedDirectly); return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(), @@ -250,7 +251,7 @@ public: bool operator==(const RefVal& X) const { return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind(); } - + void Profile(llvm::FoldingSetNodeID& ID) const { ID.Add(T); ID.AddInteger(RawKind); @@ -426,16 +427,16 @@ public: /// setRetEffect - Set the effect of the return value of the call. void setRetEffect(RetEffect E) { Ret = E; } - + /// Sets the effect on the receiver of the message. void setReceiverEffect(ArgEffect e) { Receiver = e; } - + /// getReceiverEffect - Returns the effect on the receiver of the call. /// This is only meaningful if the summary applies to an ObjCMessageExpr*. ArgEffect getReceiverEffect() const { return Receiver; } /// Test if two retain summaries are identical. Note that merely equivalent - /// summaries are not necessarily identical (for example, if an explicit + /// summaries are not necessarily identical (for example, if an explicit /// argument effect matches the default effect). bool operator==(const RetainSummary &Other) const { return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect && @@ -484,7 +485,7 @@ public: IdentifierInfo *getIdentifier() const { return II; } Selector getSelector() const { return S; } }; -} +} // end anonymous namespace namespace llvm { template <> struct DenseMapInfo<ObjCSummaryKey> { @@ -621,7 +622,7 @@ class RetainSummaryManager { ArgEffects::Factory AF; /// ScratchArgs - A holding buffer for construct ArgEffects. - ArgEffects ScratchArgs; + ArgEffects ScratchArgs; /// ObjCAllocRetE - Default return effect for methods returning Objective-C /// objects. @@ -644,7 +645,7 @@ class RetainSummaryManager { ArgEffects getArgEffects(); enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable }; - + const RetainSummary *getUnarySummary(const FunctionType* FT, UnaryFuncKind func); @@ -664,7 +665,7 @@ class RetainSummaryManager { const RetainSummary *getDoNothingSummary() { return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } - + const RetainSummary *getDefaultSummary() { return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, MayEscape); @@ -689,7 +690,7 @@ private: void addClassMethSummary(const char* Cls, const char* name, const RetainSummary *Summ, bool isNullary = true) { IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); - Selector S = isNullary ? GetNullarySelector(name, Ctx) + Selector S = isNullary ? GetNullarySelector(name, Ctx) : GetUnarySelector(name, Ctx); ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; } @@ -739,7 +740,7 @@ public: ? RetEffect::MakeGCNotOwned() : (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC) : RetEffect::MakeOwned(RetEffect::ObjC, true))), - ObjCInitRetE(gcenabled + ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() : (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC) : RetEffect::MakeOwnedWhenTrackedReceiver())) { @@ -803,7 +804,7 @@ public: bool isGCEnabled() const { return GCEnabled; } bool isARCEnabled() const { return ARCEnabled; } - + bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } @@ -966,7 +967,7 @@ void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, // Additionally, our Self Init checker already warns about it. To avoid // overwhelming the user with messages from both checkers, we model the case // of '[super init]' in cases when it is not consumed by another expression - // as if the call preserves the value of 'self'; essentially, assuming it can + // as if the call preserves the value of 'self'; essentially, assuming it can // never fail and return 'nil'. // Note, we don't want to just stop tracking the value since we want the // RetainCount checker to report leaks and use-after-free if SelfInit checker @@ -985,7 +986,6 @@ void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, ModifiableSummaryTemplate->setRetEffect(RetEffect::MakeNoRet()); } } - } } @@ -1150,7 +1150,7 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { if (S) break; - if (RetTy->isPointerType()) { + if (RetTy->isPointerType()) { // For CoreFoundation ('CF') types. if (cocoa::isRefType(RetTy, "CF", FName)) { if (isRetain(FD, FName)) { @@ -1278,14 +1278,14 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT, return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } -const RetainSummary * +const RetainSummary * RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { assert (ScratchArgs.isEmpty()); return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); } -const RetainSummary * +const RetainSummary * RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { assert (ScratchArgs.isEmpty()); return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), @@ -1331,7 +1331,7 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, // Effects on the parameters. unsigned parm_idx = 0; - for (FunctionDecl::param_const_iterator pi = FD->param_begin(), + for (FunctionDecl::param_const_iterator pi = FD->param_begin(), pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; if (pd->hasAttr<NSConsumedAttr>()) @@ -1367,8 +1367,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, // Effects on the receiver. if (MD->hasAttr<NSConsumesSelfAttr>()) - Template->setReceiverEffect(DecRefMsg); - + Template->setReceiverEffect(DecRefMsg); + // Effects on the parameters. unsigned parm_idx = 0; for (ObjCMethodDecl::param_const_iterator @@ -1376,9 +1376,9 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; if (pd->hasAttr<NSConsumedAttr>()) - Template->addArg(AF, parm_idx, DecRefMsg); + Template->addArg(AF, parm_idx, DecRefMsg); else if (pd->hasAttr<CFConsumedAttr>()) { - Template->addArg(AF, parm_idx, DecRef); + Template->addArg(AF, parm_idx, DecRef); } else if (pd->hasAttr<CFReturnsRetainedAttr>()) { QualType PointeeTy = pd->getType()->getPointeeType(); if (!PointeeTy.isNull()) @@ -1415,7 +1415,7 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, if (cocoa::isCocoaObjectRef(RetTy)) ResultEff = RetEffect::MakeNotOwned(RetEffect::ObjC); else if (coreFoundation::isCFObjectRef(RetTy)) { - // ObjCMethodDecl currently doesn't consider CF objects as valid return + // ObjCMethodDecl currently doesn't consider CF objects as valid return // values for alloc, new, copy, or mutableCopy, so we have to // double-check with the selector. This is ugly, but there aren't that // many Objective-C methods that return CF objects, right? @@ -1428,11 +1428,11 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, ResultEff = RetEffect::MakeOwned(RetEffect::CF, true); break; default: - ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); + ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); break; } } else { - ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); + ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); } } break; @@ -1749,7 +1749,7 @@ namespace { SymbolRef Sym; const SummaryLogTy &SummaryLog; bool GCEnabled; - + public: CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log) : Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {} @@ -1869,7 +1869,7 @@ void CFRefReport::addGCModeDescription(const LangOptions &LOpts, static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. - return isa<IntegerLiteral>(E) || + return isa<IntegerLiteral>(E) || isa<CharacterLiteral>(E) || isa<FloatingLiteral>(E) || isa<ObjCBoolLiteralExpr>(E) || @@ -1948,7 +1948,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, else if (isa<ObjCIvarRefExpr>(S)) { os << "Object loaded from instance variable"; } - else { + else { if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available). SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); @@ -2192,6 +2192,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, return P; } +namespace { // Find the first node in the current function context that referred to the // tracked symbol and the memory location that value was stored to. Note, the // value is only reported if the allocation occurred in the same function as @@ -2206,6 +2207,7 @@ struct AllocationInfo { const LocationContext *InInterestingMethodContext) : N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} }; +} // end anonymous namespace static AllocationInfo GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, @@ -2228,7 +2230,7 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, StoreManager::FindUniqueBinding FB(Sym); StateMgr.iterBindings(St, FB); - + if (FB) { const MemRegion *R = FB.getRegion(); const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>(); @@ -2345,10 +2347,10 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, // objects. Only "copy", "alloc", "retain" and "new" transfer ownership // to the caller for NS objects. const Decl *D = &EndN->getCodeDecl(); - + os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " : " is returned from a function "); - + if (D->hasAttr<CFReturnsNotRetainedAttr>()) os << "that is annotated as CF_RETURNS_NOT_RETAINED"; else if (D->hasAttr<NSReturnsNotRetainedAttr>()) @@ -2385,7 +2387,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, } CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, - bool GCEnabled, const SummaryLogTy &Log, + bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx, bool IncludeAllocationLine) @@ -2414,7 +2416,7 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, // FIXME: This will crash the analyzer if an allocation comes from an // implicit call (ex: a destructor call). // (Currently there are no such allocations in Cocoa, though.) - const Stmt *AllocStmt = 0; + const Stmt *AllocStmt = nullptr; ProgramPoint P = AllocNode->getLocation(); if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) AllocStmt = Exit->getCalleeContext()->getCallSite(); @@ -2492,7 +2494,7 @@ class RetainCountChecker /// the allocation line. mutable bool IncludeAllocationLine; -public: +public: RetainCountChecker(AnalyzerOptions &AO) : ShouldResetSummaryLog(false), IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {} @@ -2617,7 +2619,7 @@ public: void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - + void checkSummary(const RetainSummary &Summ, const CallEvent &Call, CheckerContext &C) const; @@ -2630,13 +2632,13 @@ public: ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const; - ProgramStateRef + ProgramStateRef checkRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const; - + bool wantsRegionChangeUpdate(ProgramStateRef state) const { return true; } @@ -2645,7 +2647,7 @@ public: void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C, ExplodedNode *Pred, RetEffect RE, RefVal X, SymbolRef Sym, ProgramStateRef state) const; - + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndFunction(CheckerContext &C) const; @@ -2656,7 +2658,7 @@ public: void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; - + void processObjCLiterals(CheckerContext &C, const Expr *Ex) const; const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; @@ -2678,7 +2680,7 @@ public: } // end anonymous namespace namespace { -class StopTrackingCallback : public SymbolVisitor { +class StopTrackingCallback final : public SymbolVisitor { ProgramStateRef state; public: StopTrackingCallback(ProgramStateRef st) : state(st) {} @@ -2740,21 +2742,21 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); if (!BE) return; - + ArgEffect AE = IncRef; - + switch (BE->getBridgeKind()) { case clang::OBC_Bridge: // Do nothing. return; case clang::OBC_BridgeRetained: AE = IncRef; - break; + break; case clang::OBC_BridgeTransfer: AE = DecRefBridgedTransferred; break; } - + ProgramStateRef state = C.getState(); SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol(); if (!Sym) @@ -2765,7 +2767,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, RefVal::Kind hasErr = (RefVal::Kind) 0; state = updateSymbol(state, Sym, *T, AE, hasErr, C); - + if (hasErr) { // FIXME: If we get an error during a bridge cast, should we report it? return; @@ -2777,7 +2779,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, void RetainCountChecker::processObjCLiterals(CheckerContext &C, const Expr *Ex) const { ProgramStateRef state = C.getState(); - const ExplodedNode *pred = C.getPredecessor(); + const ExplodedNode *pred = C.getPredecessor(); for (const Stmt *Child : Ex->children()) { SVal V = state->getSVal(Child, pred->getLocationContext()); if (SymbolRef sym = V.getAsSymbol()) @@ -2790,17 +2792,17 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C, } } } - + // Return the object as autoreleased. // RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC); - if (SymbolRef sym = + if (SymbolRef sym = state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { QualType ResultTy = Ex->getType(); state = setRefBinding(state, sym, RefVal::makeNotOwned(RetEffect::ObjC, ResultTy)); } - - C.addTransition(state); + + C.addTransition(state); } void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL, @@ -2817,7 +2819,7 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, CheckerContext &C) const { - const ExplodedNode *Pred = C.getPredecessor(); + const ExplodedNode *Pred = C.getPredecessor(); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -2966,7 +2968,7 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, if (Sym) state = removeRefBinding(state, Sym); } - + C.addTransition(state); } @@ -3062,7 +3064,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { if (ReceiverIsTracked) - RE = getSummaryManager(C).getObjAllocRetEffect(); + RE = getSummaryManager(C).getObjAllocRetEffect(); else RE = RetEffect::MakeNoRet(); } @@ -3129,8 +3131,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, } } - -ProgramStateRef +ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, RefVal V, ArgEffect E, RefVal::Kind &hasErr, CheckerContext &C) const { @@ -3306,7 +3307,7 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St, if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None) return; - ExplodedNode *N = C.generateSink(St); + ExplodedNode *N = C.generateErrorNode(St); if (!N) return; @@ -3388,7 +3389,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { isMakeCollectable(FD, FName); } } - + if (!canEval) return false; @@ -3531,7 +3532,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *Pred, RetEffect RE, RefVal X, SymbolRef Sym, - ProgramStateRef state) const { + ProgramStateRef state) const { // HACK: Ignore retain-count issues on values accessed through ivars, // because of cases like this: // [_contentView retain]; @@ -3669,7 +3670,6 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const { - // FIXME: We may add to the interface of evalAssume the list of symbols // whose assumptions have changed. For now we just iterate through the // bindings and check if any of the tracked symbols are NULL. This isn't @@ -3700,7 +3700,7 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, return state; } -ProgramStateRef +ProgramStateRef RetainCountChecker::checkRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, @@ -3810,7 +3810,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, return nullptr; } -ProgramStateRef +ProgramStateRef RetainCountChecker::handleSymbolDeath(ProgramStateRef state, SymbolRef sid, RefVal V, SmallVectorImpl<SymbolRef> &Leaked) const { @@ -3890,7 +3890,7 @@ void RetainCountChecker::checkEndFunction(CheckerContext &Ctx) const { // and suggest annotations. if (LCtx->getParent()) return; - + B = state->get<RefBindings>(); SmallVector<SymbolRef, 10> Leaked; @@ -3910,7 +3910,7 @@ RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const { sym->dumpToStream(out); tag = new CheckerProgramPointTag(this, out.str()); } - return tag; + return tag; } void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, @@ -3993,7 +3993,9 @@ void ento::registerRetainCountChecker(CheckerManager &Mgr) { // Implementation of the CallEffects API. //===----------------------------------------------------------------------===// -namespace clang { namespace ento { namespace objc_retain { +namespace clang { +namespace ento { +namespace objc_retain { // This is a bit gross, but it allows us to populate CallEffects without // creating a bunch of accessors. This kind is very localized, so the @@ -4022,4 +4024,6 @@ CallEffects CallEffects::getEffect(const FunctionDecl *FD) { #undef createCallEffect -}}} +} // end namespace objc_retain +} // end namespace ento +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index acbd0d9..19fa0fb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -23,7 +23,7 @@ using namespace clang; using namespace ento; namespace { -class ReturnPointerRangeChecker : +class ReturnPointerRangeChecker : public Checker< check::PreStmt<ReturnStmt> > { mutable std::unique_ptr<BuiltinBug> BT; @@ -39,7 +39,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, const Expr *RetE = RS->getRetValue(); if (!RetE) return; - + SVal V = state->getSVal(RetE, C.getLocationContext()); const MemRegion *R = V.getAsRegion(); @@ -62,11 +62,11 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(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) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 2668ac1..c5e826a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -80,7 +80,7 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, const Expr *TrackingE = nullptr) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index c22e78b..7026a2e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -92,7 +92,7 @@ public: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) namespace { -class StopTrackingCallback : public SymbolVisitor { +class StopTrackingCallback final : public SymbolVisitor { ProgramStateRef state; public: StopTrackingCallback(ProgramStateRef st) : state(st) {} @@ -200,7 +200,9 @@ void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, State = State->remove<StreamMap>(Sym); } - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; reportLeaks(LeakedStreams, C, N); } @@ -208,7 +210,7 @@ void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const { // We reached a bug, stop exploring the path here by generating a sink. - ExplodedNode *ErrNode = C.generateSink(); + ExplodedNode *ErrNode = C.generateErrorNode(); // If we've already reached this node on another path, return. if (!ErrNode) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 813c811..79fc701 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines stack address leak checker, which checks if an invalid +// 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. // //===----------------------------------------------------------------------===// @@ -49,20 +49,20 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, SourceManager &SM = Ctx.getSourceManager(); SourceRange range; os << "Address of "; - + // Check if the region is a compound literal. - if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { + 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.getExpansionLineNumber(CL->getLocStart()) - << " returned to caller"; + << " 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(); + range = ARE->getSourceRange(); os << "stack memory allocated by call to alloca() on line " << SM.getExpansionLineNumber(L); } @@ -87,14 +87,14 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, } else { llvm_unreachable("Invalid region in ReturnStackAddressChecker."); - } - + } + return range; } void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -118,7 +118,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion * void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { - + const Expr *RetE = RS->getRetValue(); if (!RetE) return; @@ -130,10 +130,10 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (!R) return; - + const StackSpaceRegion *SS = dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace()); - + if (!SS) return; @@ -156,6 +156,15 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) return; + // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied + // so the stack address is not escaping here. + if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { + if (isa<BlockDataRegion>(R) && + ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) { + return; + } + } + EmitStackError(C, R, RetE); } @@ -175,35 +184,35 @@ void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { Ctx(CC), CurSFC(CC.getLocationContext()->getCurrentStackFrame()) {} - + bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) override { if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) return true; - + const MemRegion *vR = val.getAsRegion(); if (!vR) return true; - + // Under automated retain release, it is okay to assign a block // directly to a global variable. if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && isa<BlockDataRegion>(vR)) return true; - if (const StackSpaceRegion *SSR = + 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(Ctx); state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); @@ -211,7 +220,7 @@ void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { return; // Generate an error node. - ExplodedNode *N = Ctx.addTransition(state); + ExplodedNode *N = Ctx.generateNonFatalErrorNode(state); if (!N) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2109a75..82b01fe 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -43,8 +43,8 @@ struct StreamState { 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 getOpenFailed(const Stmt *s) { + return StreamState(OpenFailed, s); } static StreamState getEscaped(const Stmt *s) { return StreamState(Escaped, s); @@ -59,14 +59,14 @@ struct StreamState { class StreamChecker : public Checker<eval::Call, check::DeadSymbols > { mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, - *II_fwrite, - *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, + *II_fwrite, + *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, *II_clearerr, *II_feof, *II_ferror, *II_fileno; mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, BT_doubleclose, BT_ResourceLeak; public: - StreamChecker() + StreamChecker() : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr), II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr), II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr), @@ -93,10 +93,10 @@ private: void Fileno(CheckerContext &C, const CallExpr *CE) const; void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; - - ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, + + ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, CheckerContext &C) const; - ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, + ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, CheckerContext &C) const; }; @@ -216,13 +216,13 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { C.blockCount()) .castAs<DefinedSVal>(); state = state->BindExpr(CE, C.getLocationContext(), RetVal); - + ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. ProgramStateRef stateNotNull, stateNull; std::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 = @@ -271,7 +271,7 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { if (x >= 0 && x <= 2) return; - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_illegalwhence) BT_illegalwhence.reset( new BuiltinBug(this, "Illegal whence argument", @@ -349,7 +349,7 @@ ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (!stateNotNull && stateNull) { - if (ExplodedNode *N = C.generateSink(stateNull)) { + if (ExplodedNode *N = C.generateErrorNode(stateNull)) { if (!BT_nullfp) BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", "Stream pointer might be NULL.")); @@ -368,17 +368,17 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol(); if (!Sym) return state; - + const StreamState *SS = state->get<StreamMap>(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(); + ExplodedNode *N = C.generateErrorNode(); if (N) { if (!BT_doubleclose) BT_doubleclose.reset(new BuiltinBug( @@ -389,7 +389,7 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, } return nullptr; } - + // Close the File Descriptor. return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); } @@ -406,7 +406,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, continue; if (SS->isOpened()) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (N) { if (!BT_ResourceLeak) BT_ResourceLeak.reset(new BuiltinBug( diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp index 6e24775..2e05290 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -48,7 +48,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E, return; if (State->isTainted(E, C.getLocationContext())) { - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, "tainted",N); report->addRange(E->getSourceRange()); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index 638701d..b794d2f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -167,7 +167,7 @@ bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var, } void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { - if (ExplodedNode *N = C.generateSink(C.getState())) { + if (ExplodedNode *N = C.generateErrorNode(C.getState())) { if (!DivZeroBug) DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 1d8ef99..ed17610 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -30,7 +30,7 @@ class UndefBranchChecker : public Checker<check::BranchCondition> { ProgramStateRef St; const LocationContext *LCtx; - FindUndefExpr(ProgramStateRef S, const LocationContext *L) + FindUndefExpr(ProgramStateRef S, const LocationContext *L) : St(S), LCtx(L) {} const Expr *FindExpr(const Expr *Ex) { @@ -45,7 +45,7 @@ class UndefBranchChecker : public Checker<check::BranchCondition> { return Ex; } - bool MatchesCriteria(const Expr *Ex) { + bool MatchesCriteria(const Expr *Ex) { return St->getSVal(Ex, LCtx).isUndef(); } }; @@ -62,7 +62,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, if (X.isUndef()) { // Generate a sink node, which implicitly marks both outgoing branches as // infeasible. - ExplodedNode *N = Ctx.generateSink(); + ExplodedNode *N = Ctx.generateErrorNode(); if (N) { if (!BT) BT.reset(new BuiltinBug( diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 53fd069..17fe861 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -74,7 +74,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, // Get the VarRegion associated with VD in the local stack frame. if (Optional<UndefinedVal> V = state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "uninitialized variable captured by block")); @@ -83,7 +83,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, SmallString<128> buf; llvm::raw_svector_ostream os(buf); - os << "Variable '" << VD->getName() + os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; auto R = llvm::make_unique<BugReport>(*BT, os.str(), N); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 5353310..38d2aa6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This defines UndefResultChecker, a builtin check in ExprEngine that +// This defines UndefResultChecker, a builtin check in ExprEngine that // performs checks for undefined results of non-assignment binary operators. // //===----------------------------------------------------------------------===// @@ -25,7 +25,7 @@ using namespace clang; using namespace ento; namespace { -class UndefResultChecker +class UndefResultChecker : public Checker< check::PostStmt<BinaryOperator> > { mutable std::unique_ptr<BugType> BT; @@ -50,10 +50,10 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, return; // Generate an error node. - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; - + if (!BT) BT.reset( new BuiltinBug(this, "Result of operation is garbage or undefined")); @@ -62,7 +62,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = nullptr; bool isLeft = true; - + if (state->getSVal(B->getLHS(), LCtx).isUndef()) { Ex = B->getLHS()->IgnoreParenCasts(); isLeft = true; @@ -71,13 +71,13 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, 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 '" @@ -91,7 +91,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, } else bugreporter::trackNullOrUndefValue(N, B, *report); - + C.emitReport(std::move(report)); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index ba4daf8..fe07eaf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -32,7 +32,7 @@ public: }; } // end anonymous namespace -void +void UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const { const Expr *Index = A->getIdx(); @@ -46,7 +46,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, if (Ctor->isDefaulted()) return; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 81c96c4..7a31efc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -46,7 +46,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, if (C.getCalleeName(EnclosingFunctionDecl) == "swap") return; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index a799b4c..4b78c20 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -77,7 +77,7 @@ void UnixAPIChecker::ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg, SourceRange SR) const { - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); if (!N) return; @@ -114,7 +114,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. if (!Val_O_CREAT.hasValue()) { - if (C.getASTContext().getTargetInfo().getTriple().getVendor() + if (C.getASTContext().getTargetInfo().getTriple().getVendor() == llvm::Triple::Apple) Val_O_CREAT = 0x0200; else { @@ -182,7 +182,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; - ExplodedNode *N = C.generateSink(state); + ExplodedNode *N = C.generateErrorNode(state); if (!N) return; @@ -220,7 +220,7 @@ static bool IsZeroByteAllocation(ProgramStateRef state, ProgramStateRef *falseState) { std::tie(*trueState, *falseState) = state->assume(argVal.castAs<DefinedSVal>()); - + return (*falseState && !*trueState); } @@ -231,7 +231,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, ProgramStateRef falseState, const Expr *arg, const char *fn_name) const { - ExplodedNode *N = C.generateSink(falseState); + ExplodedNode *N = C.generateErrorNode(falseState); if (!N) return false; @@ -239,7 +239,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); SmallString<256> S; - llvm::raw_svector_ostream os(S); + llvm::raw_svector_ostream os(S); os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N); @@ -272,13 +272,13 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, // Is the value perfectly constrained to zero? if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { - (void) ReportZeroByteAllocation(C, falseState, arg, fn); + (void) ReportZeroByteAllocation(C, falseState, arg, fn); return; } // Assume the value is non-zero going forward. assert(trueState); if (trueState != state) - C.addTransition(trueState); + C.addTransition(trueState); } void UnixAPIChecker::CheckCallocZero(CheckerContext &C, diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index d78de3c..a03abce 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -54,7 +54,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng) const { CFGBlocksSet reachable, visited; - + if (Eng.hasWorkRemaining()) return; @@ -88,7 +88,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, // Bail out if we didn't get the CFG or the ParentMap. if (!D || !C || !PM) return; - + // Don't do anything for template instantiations. Proving that code // in a template instantiation is unreachable means proving that it is // unreachable in all instantiations. @@ -235,12 +235,9 @@ bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, return false; // Run each of the checks on the conditions - if (containsMacro(cond) || containsEnum(cond) - || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) - || containsStmt<UnaryExprOrTypeTraitExpr>(cond)) - return true; - - return false; + return containsMacro(cond) || containsEnum(cond) || + containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) || + containsStmt<UnaryExprOrTypeTraitExpr>(cond); } // Returns true if the given CFGBlock is empty diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 80384bb..e3b2ed2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This defines VLASizeChecker, a builtin check in ExprEngine that +// 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. @@ -46,7 +46,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind, ProgramStateRef State, CheckerContext &C) const { // Generate an error node. - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); if (!N) return; @@ -82,7 +82,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind, void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (!DS->isSingleDecl()) return; - + const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; @@ -106,7 +106,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // warned about that already. if (sizeV.isUnknown()) return; - + // Check if the size is tainted. if (state->isTainted(sizeV)) { reportBug(VLA_Tainted, SE, nullptr, C); @@ -123,7 +123,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { reportBug(VLA_Zero, SE, stateZero, C); return; } - + // From this point on, assume that the size is not zero. state = stateNotZero; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp new file mode 100644 index 0000000..26ffee8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -0,0 +1,218 @@ +//===- VforkChecker.cpp -------- Vfork usage checks --------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines vfork checker which checks for dangerous uses of vfork. +// Vforked process shares memory (including stack) with parent so it's +// range of actions is significantly limited: can't write variables, +// can't call functions not in whitelist, etc. For more details, see +// http://man7.org/linux/man-pages/man2/vfork.2.html +// +// This checker checks for prohibited constructs in vforked process. +// The state transition diagram: +// PARENT ---(vfork() == 0)--> CHILD +// | +// --(*p = ...)--> bug +// | +// --foo()--> bug +// | +// --return--> bug +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace ento; + +namespace { + +class VforkChecker : public Checker<check::PreCall, check::PostCall, + check::Bind, check::PreStmt<ReturnStmt>> { + mutable std::unique_ptr<BuiltinBug> BT; + mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist; + mutable const IdentifierInfo *II_vfork; + + static bool isChildProcess(const ProgramStateRef State); + + bool isVforkCall(const Decl *D, CheckerContext &C) const; + bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const; + + void reportBug(const char *What, CheckerContext &C, + const char *Details = 0) const; + +public: + VforkChecker() : II_vfork(0) {} + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; +}; + +} // end anonymous namespace + +// This trait holds region of variable that is assigned with vfork's +// return value (this is the only region child is allowed to write). +// VFORK_RESULT_INVALID means that we are in parent process. +// VFORK_RESULT_NONE means that vfork's return value hasn't been assigned. +// Other values point to valid regions. +REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion, const void *) +#define VFORK_RESULT_INVALID 0 +#define VFORK_RESULT_NONE ((void *)(uintptr_t)1) + +bool VforkChecker::isChildProcess(const ProgramStateRef State) { + return State->get<VforkResultRegion>() != VFORK_RESULT_INVALID; +} + +bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const { + auto FD = dyn_cast_or_null<FunctionDecl>(D); + if (!FD || !C.isCLibraryFunction(FD)) + return false; + + if (!II_vfork) { + ASTContext &AC = C.getASTContext(); + II_vfork = &AC.Idents.get("vfork"); + } + + return FD->getIdentifier() == II_vfork; +} + +// Returns true iff ok to call function after successful vfork. +bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II, + CheckerContext &C) const { + if (VforkWhitelist.empty()) { + // According to manpage. + const char *ids[] = { + "_exit", + "_Exit", + "execl", + "execlp", + "execle", + "execv", + "execvp", + "execvpe", + 0, + }; + + ASTContext &AC = C.getASTContext(); + for (const char **id = ids; *id; ++id) + VforkWhitelist.insert(&AC.Idents.get(*id)); + } + + return VforkWhitelist.count(II); +} + +void VforkChecker::reportBug(const char *What, CheckerContext &C, + const char *Details) const { + if (ExplodedNode *N = C.generateErrorNode(C.getState())) { + if (!BT) + BT.reset(new BuiltinBug(this, + "Dangerous construct in a vforked process")); + + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + + os << What << " is prohibited after a successful vfork"; + + if (Details) + os << "; " << Details; + + auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); + // TODO: mark vfork call in BugReportVisitor + C.emitReport(std::move(Report)); + } +} + +// Detect calls to vfork and split execution appropriately. +void VforkChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // We can't call vfork in child so don't bother + // (corresponding warning has already been emitted in checkPreCall). + ProgramStateRef State = C.getState(); + if (isChildProcess(State)) + return; + + if (!isVforkCall(Call.getDecl(), C)) + return; + + // Get return value of vfork. + SVal VforkRetVal = Call.getReturnValue(); + Optional<DefinedOrUnknownSVal> DVal = + VforkRetVal.getAs<DefinedOrUnknownSVal>(); + if (!DVal) + return; + + // Get assigned variable. + const ParentMap &PM = C.getLocationContext()->getParentMap(); + const Stmt *P = PM.getParentIgnoreParenCasts(Call.getOriginExpr()); + const VarDecl *LhsDecl; + std::tie(LhsDecl, std::ignore) = parseAssignment(P); + + // Get assigned memory region. + MemRegionManager &M = C.getStoreManager().getRegionManager(); + const MemRegion *LhsDeclReg = + LhsDecl + ? M.getVarRegion(LhsDecl, C.getLocationContext()) + : (const MemRegion *)VFORK_RESULT_NONE; + + // Parent branch gets nonzero return value (according to manpage). + ProgramStateRef ParentState, ChildState; + std::tie(ParentState, ChildState) = C.getState()->assume(*DVal); + C.addTransition(ParentState); + ChildState = ChildState->set<VforkResultRegion>(LhsDeclReg); + C.addTransition(ChildState); +} + +// Prohibit calls to non-whitelist functions in child process. +void VforkChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (isChildProcess(State) + && !isCallWhitelisted(Call.getCalleeIdentifier(), C)) + reportBug("This function call", C); +} + +// Prohibit writes in child process (except for vfork's lhs). +void VforkChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!isChildProcess(State)) + return; + + const MemRegion *VforkLhs = + static_cast<const MemRegion *>(State->get<VforkResultRegion>()); + const MemRegion *MR = L.getAsRegion(); + + // Child is allowed to modify only vfork's lhs. + if (!MR || MR == VforkLhs) + return; + + reportBug("This assignment", C); +} + +// Prohibit return from function in child process. +void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (isChildProcess(State)) + reportBug("Return", C, "call _exit() instead"); +} + +void ento::registerVforkChecker(CheckerManager &mgr) { + mgr.registerChecker<VforkChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index f6ef4ae..55025030 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a checker that checks virtual function calls during +// This file defines a checker that checks virtual function calls during // construction or destruction of C++ objects. // //===----------------------------------------------------------------------===// @@ -37,13 +37,13 @@ class WalkAST : public StmtVisitor<WalkAST> { /// A vector representing the worklist which has a chain of CallExprs. DFSWorkList WList; - + // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the // body has not been visited yet. // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the // body has been visited. enum Kind { NotVisited, - PreVisited, /**< A CallExpr to this FunctionDecl is in the + PreVisited, /**< A CallExpr to this FunctionDecl is in the worklist, but the body has not yet been visited. */ PostVisited /**< A CallExpr to this FunctionDecl is in the @@ -57,7 +57,7 @@ class WalkAST : public StmtVisitor<WalkAST> { /// generating bug reports. This is null while visiting the body of a /// constructor or destructor. const CallExpr *visitingCallExpr; - + public: WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac) @@ -70,7 +70,7 @@ public: void Enqueue(WorkListUnit WLUnit) { const FunctionDecl *FD = WLUnit->getDirectCallee(); if (!FD || !FD->getBody()) - return; + return; Kind &K = VisitedFunctions[FD]; if (K != NotVisited) return; @@ -81,9 +81,9 @@ public: /// This method returns an item from the worklist without removing it. WorkListUnit Dequeue() { assert(!WList.empty()); - return WList.back(); + return WList.back(); } - + void Execute() { while (hasWork()) { WorkListUnit WLUnit = Dequeue(); @@ -95,7 +95,7 @@ public: // Visit the body. SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit); Visit(FD->getBody()); - + // Mark the function as being PostVisited to indicate we have // scanned the body. VisitedFunctions[FD] = PostVisited; @@ -114,7 +114,7 @@ public: void VisitCXXMemberCallExpr(CallExpr *CE); void VisitStmt(Stmt *S) { VisitChildren(S); } void VisitChildren(Stmt *S); - + void ReportVirtualCall(const CallExpr *CE, bool isPure); }; @@ -138,7 +138,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { VisitChildren(CE); bool callIsNonVirtual = false; - + // Several situations to elide for checking. if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { // If the member access is fully qualified (i.e., X::F), then treat @@ -170,7 +170,7 @@ void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { SmallString<100> buf; llvm::raw_svector_ostream os(buf); - + os << "Call Path : "; // Name of current visiting CallExpr. os << *CE->getDirectCallee(); @@ -190,7 +190,7 @@ void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); SourceRange R = CE->getCallee()->getSourceRange(); - + if (isPure) { os << "\n" << "Call pure virtual functions during construction or " << "destruction may leads undefined behaviour"; |