void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; ProgramStateRef state = C.getState(); if (const LockState *LState = state->get<LockMap>(lockR)) { if (LState->isUnlocked()) { if (!BT_doubleunlock) BT_doubleunlock.reset(new BugType(this, "Double unlocking", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *Report = new BugReport(*BT_doubleunlock, "This lock has already been unlocked", N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(Report); return; } else if (LState->isDestroyed()) { reportUseDestroyedBug(C, CE); return; } } LockSetTy LS = state->get<LockSet>(); // FIXME: Better analysis requires IPA for wrappers. if (!LS.isEmpty()) { const MemRegion *firstLockR = LS.getHead(); if (firstLockR != lockR) { if (!BT_lor) BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *report = new BugReport(*BT_lor, "This was not the most recently " "acquired lock. Possible lock order " "reversal", N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(report); return; } // Record that the lock was released. state = state->set<LockSet>(LS.getTail()); } state = state->set<LockMap>(lockR, LockState::getUnlocked()); C.addTransition(state); }
void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // Check if the method is annotated with analyzer_noreturn. if (const ObjCMethodDecl *MD = Msg.getDecl()) { MD = MD->getCanonicalDecl(); if (MD->hasAttr<AnalyzerNoReturnAttr>()) { C.generateSink(); return; } } // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler // handleFailureInMethod:object:file:lineNumber:description:] // -[NSAssertionHandler // handleFailureInFunction:file:lineNumber:description:] // Eventually these should be annotated with __attribute__((noreturn)). // Because ObjC messages use dynamic dispatch, it is not generally safe to // assume certain methods can't return. In cases where it is definitely valid, // see if you can mark the methods noreturn or analyzer_noreturn instead of // adding more explicit checks to this method. if (!Msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); if (!Receiver) return; if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) return; Selector Sel = Msg.getSelector(); switch (Sel.getNumArgs()) { default: return; case 4: lazyInitKeywordSelector(HandleFailureInFunctionSel, C.getASTContext(), "handleFailureInFunction", "file", "lineNumber", "description", nullptr); if (Sel != HandleFailureInFunctionSel) return; break; case 5: lazyInitKeywordSelector(HandleFailureInMethodSel, C.getASTContext(), "handleFailureInMethod", "object", "file", "lineNumber", "description", nullptr); if (Sel != HandleFailureInMethodSel) return; break; } // If we got here, it's one of the messages we care about. C.generateSink(); }
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 (!BT_undef) BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), report); report->disablePathPruning(); C.EmitReport(report); } return; } DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); // Check for null dereferences. if (!isa<Loc>(location)) return; ProgramStateRef state = C.getState(); ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { reportBug(nullState, S, C); return; } // 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() }; dispatchEvent(event); } } // From this point forward, we know that the location is not null. C.addTransition(notNullState); }
void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const { const MemRegion *LockR = Lock.getAsRegion(); if (!LockR) return; ProgramStateRef State = C.getState(); const LockState *LState = State->get<LockMap>(LockR); if (!LState || LState->isUnlocked()) { State = State->set<LockMap>(LockR, LockState::getDestroyed()); C.addTransition(State); return; } StringRef Message; if (LState->isLocked()) { Message = "This lock is still locked"; } else { Message = "This lock has already been destroyed"; } if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Destroy invalid lock", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); }
void UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const { const Expr *Index = A->getIdx(); if (!C.getSVal(Index).isUndef()) return; // Sema generates anonymous array variables for copying array struct fields. // Don't warn if we're in an implicitly-generated constructor. const Decl *D = C.getLocationContext()->getDecl(); if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) if (Ctor->isDefaulted()) return; ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT) BT.reset(new BuiltinBug(this, "Array subscript is undefined")); // Generate a report for this bug. BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); bugreporter::trackNullOrUndefValue(N, A->getIdx(), *R); C.emitReport(R); }
void VLASizeChecker::reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, CheckerContext &C) const { // Generate an error node. ExplodedNode *N = C.generateSink(State); if (!N) return; if (!BT) BT.reset(new BuiltinBug( this, "Dangerous variable-length array (VLA) declaration")); SmallString<256> buf; llvm::raw_svector_ostream os(buf); os << "Declared variable-length array (VLA) "; switch (Kind) { case VLA_Garbage: os << "uses a garbage value as its size"; break; case VLA_Zero: os << "has zero size"; break; case VLA_Tainted: os << "has tainted size"; break; } BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(SizeE->getSourceRange()); bugreporter::trackNullOrUndefValue(N, SizeE, *report); C.emitReport(report); return; }
void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const { const MemRegion *LockR = Lock.getAsRegion(); if (!LockR) return; ProgramStateRef State = C.getState(); const struct LockState *LState = State->get<LockMap>(LockR); if (!LState || LState->isDestroyed()) { State = State->set<LockMap>(LockR, LockState::getUnlocked()); C.addTransition(State); return; } StringRef Message; if (LState->isLocked()) { Message = "This lock is still being held"; } else { Message = "This lock has already been initialized"; } if (!BT_initlock) BT_initlock.reset(new BugType(this, "Init invalid lock", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *Report = new BugReport(*BT_initlock, Message, N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(Report); }
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind) const { ExplodedNode *errorNode = checkerContext.generateSink(errorState); if (!errorNode) return; if (!BT) BT.reset(new BuiltinBug("Out-of-bound access")); // FIXME: This diagnostics are preliminary. We should get far better // diagnostics for explaining buffer overruns. SmallString<256> buf; llvm::raw_svector_ostream os(buf); os << "Out of bound memory access "; switch (kind) { case OOB_Precedes: os << "(accessed memory precedes memory block)"; break; case OOB_Excedes: os << "(access exceeds upper limit of memory block)"; break; case OOB_Tainted: os << "(index is tainted)"; break; } checkerContext.emitReport(new BugReport(*BT, os.str(), errorNode)); }
void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal, const SVal &RVal, OverloadedOperatorKind Op) const { // Record the operands and the operator of the comparison for the next // evalAssume, if the result is a symbolic expression. If it is a concrete // value (only one branch is possible), then transfer the state between // the operands according to the operator and the result auto State = C.getState(); if (const auto *Condition = RetVal.getAsSymbolicExpression()) { const auto *LPos = getIteratorPosition(State, LVal); const auto *RPos = getIteratorPosition(State, RVal); if (!LPos && !RPos) return; State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual); C.addTransition(State); } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { if ((State = processComparison( State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal), (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) { C.addTransition(State); } else { C.generateSink(State, C.getPredecessor()); } } }
SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, const Expr* Arg, bool IssueWarning) const { const ProgramState *State = C.getState(); SVal AddrVal = State->getSVal(Arg->IgnoreParenCasts()); Loc *AddrLoc = dyn_cast<Loc>(&AddrVal); if (!AddrLoc && !IssueWarning) return 0; // If the Expr is not a location, issue a warning. if (!AddrLoc) { assert(IssueWarning); if (ExplodedNode *N = C.generateSink(State)) { initBugType(); BugReport *report = new BugReport(*BT, "Pointer argument is expected.",N); report->addRange(Arg->getSourceRange()); C.EmitReport(report); } return 0; } SVal Val = State->getSVal(*AddrLoc); return Val.getAsSymbol(); }
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { // TODO: Clean up the state. for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), E = SymReaper.dead_end(); I != E; ++I) { SymbolRef Sym = *I; ProgramStateRef state = C.getState(); const StreamState *SS = state->get<StreamMap>(Sym); // TODO: Shouldn't we have a continue here? if (!SS) return; if (SS->isOpened()) { ExplodedNode *N = C.generateSink(); if (N) { if (!BT_ResourceLeak) BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", "Opened File never closed. Potential Resource leak.")); BugReport *R = new BugReport(*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N); C.emitReport(R); } } } }
ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, CheckerContext &C) const { SymbolRef Sym = 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(); if (N) { if (!BT_doubleclose) BT_doubleclose.reset(new BuiltinBug("Double fclose", "Try to close a file Descriptor already" " closed. Cause undefined behaviour.")); BugReport *R = new BugReport(*BT_doubleclose, BT_doubleclose->getDescription(), N); C.emitReport(R); } return NULL; } // Close the File Descriptor. return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); }
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, ProgramStateRef state, const ObjCMethodCall &Msg) const { ASTContext &Ctx = C.getASTContext(); static CheckerProgramPointTag Tag(this, "NilReceiver"); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. QualType RetTy = Msg.getResultType(); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); const LocationContext *LCtx = C.getLocationContext(); if (CanRetTy->isStructureOrClassType()) { // Structure returns are safe since the compiler zeroes them out. SVal V = C.getSValBuilder().makeZeroVal(RetTy); C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); return; } // Other cases: check if sizeof(return type) > sizeof(void*) if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() .isConsumedExpr(Msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); if (CanRetTy.getTypePtr()->isReferenceType()|| (voidPtrSize < returnTypeSize && !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && (Ctx.FloatTy == CanRetTy || Ctx.DoubleTy == CanRetTy || Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy)))) { if (ExplodedNode *N = C.generateSink(state, nullptr, &Tag)) emitNilReceiverBug(C, Msg, N); return; } // Handle the safe cases where the return value is 0 if the // receiver is nil. // // FIXME: For now take the conservative approach that we only // return null values if we *know* that the receiver is nil. // This is because we can have surprises like: // // ... = [[NSScreens screens] objectAtIndex:0]; // // What can happen is that [... screens] could return nil, but // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // SVal V = C.getSValBuilder().makeZeroVal(RetTy); C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); return; } C.addTransition(state); }
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) return; // Do not report assignments of uninitialized values inside swap functions. // This should allow to swap partially uninitialized structs // (radar://14129997) if (const FunctionDecl *EnclosingFunctionDecl = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) if (C.getCalleeName(EnclosingFunctionDecl) == "swap") return; ExplodedNode *N = C.generateSink(); if (!N) return; const char *str = "Assigned value is garbage or undefined"; if (!BT) BT.reset(new BuiltinBug(str)); // Generate a report for this bug. const Expr *ex = 0; while (StoreE) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { ProgramStateRef state = C.getState(); if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) { str = "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; ex = B->getLHS(); break; } } ex = B->getRHS(); break; } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } break; } BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); bugreporter::trackNullOrUndefValue(N, ex, *R); } C.emitReport(R); }
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { SVal recVal = msg.getReceiverSVal(); if (recVal.isUndef()) { if (ExplodedNode *N = C.generateSink()) { BugType *BT = 0; switch (msg.getMessageKind()) { case OCM_Message: if (!BT_msg_undef) BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " "is an uninitialized value")); BT = BT_msg_undef.get(); break; case OCM_PropertyAccess: if (!BT_objc_prop_undef) BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " "uninitialized object " "pointer")); BT = BT_objc_prop_undef.get(); break; case OCM_Subscript: if (!BT_objc_subscript_undef) BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an " "uninitialized object " "pointer")); BT = BT_objc_subscript_undef.get(); break; } assert(BT && "Unknown message kind."); BugReport *R = new BugReport(*BT, BT->getName(), N); const ObjCMessageExpr *ME = msg.getOriginExpr(); R->addRange(ME->getReceiverRange()); // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. if (const Expr *ReceiverE = ME->getInstanceReceiver()) R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ReceiverE, R)); C.EmitReport(R); } return; } else { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef state = C.getState(); ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // Handle receiver must be nil. if (nilState && !notNilState) { HandleNilReceiver(C, state, msg); return; } } }
void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, CheckerContext &C) const { // Check for out of bound array element access. const MemRegion *R = l.getAsRegion(); if (!R) return; const ElementRegion *ER = dyn_cast<ElementRegion>(R); if (!ER) return; // Get the index of the accessed element. DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); // Zero index is always in bound, this also passes ElementRegions created for // pointer casts. if (Idx.isZeroConstant()) return; const GRState *state = C.getState(); // Get the size of the array. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Out-of-bound array access", "Access out-of-bound array element (buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); report->addRange(C.getStmt()->getSourceRange()); C.EmitReport(report); return; } // Array bound check succeeded. From this point forward the array bound // should always succeed. assert(StInBound); C.addTransition(StInBound); }
void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); // FIXME: Handle 'super'? if (const Expr *receiver = msg.getInstanceReceiver()) { SVal recVal = state->getSVal(receiver, LCtx); if (recVal.isUndef()) { if (ExplodedNode *N = C.generateSink()) { BugType *BT = 0; if (msg.isPureMessageExpr()) { if (!BT_msg_undef) BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " "is an uninitialized value")); BT = BT_msg_undef.get(); } else { if (!BT_objc_prop_undef) BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " "uninitialized object pointer")); BT = BT_objc_prop_undef.get(); } BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(receiver->getSourceRange()); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver, R)); C.EmitReport(R); } return; } else { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // Handle receiver must be nil. if (nilState && !notNilState) { HandleNilReceiver(C, state, msg); return; } } } const char *bugDesc = msg.isPropertySetter() ? "Argument for property setter is an uninitialized value" : "Argument in message expression is an uninitialized value"; // Check for any arguments that are uninitialized/undefined. PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx), bugDesc, BT_msg_arg); }
void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE) { ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *R = new BugReport(*BT, BT->getName(), N); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, bugreporter::GetCalleeExpr(N))); C.EmitReport(R); }
void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { ProgramStateRef state = C.getState(); const Expr *RetE = RS->getRetValue(); if (!RetE) return; SVal V = state->getSVal(RetE, C.getLocationContext()); const MemRegion *R = V.getAsRegion(); const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); if (!ER) return; DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); // Zero index is always in bound, this also passes ElementRegions created for // pointer casts. if (Idx.isZeroConstant()) return; // FIXME: All of this out-of-bounds checking should eventually be refactored // into a common place. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) return; // FIXME: This bug correspond to CWE-466. Eventually we should have bug // types explicitly reference such exploit categories (when applicable). if (!BT) BT.reset(new BuiltinBug( this, "Return of pointer value outside of expected range", "Returned pointer value points outside the original object " "(potential buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); C.emitReport(std::move(report)); } }
void NilArgChecker::WarnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned int Arg, FoundationClass Class, bool CanBeSubscript) const { // Check if the argument is nil. ProgramStateRef State = C.getState(); if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; if (!BT) BT.reset(new APIMisuse("nil argument")); if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { if (Class == FC_NSArray) { os << "Array element cannot be nil"; } else if (Class == FC_NSDictionary) { if (Arg == 0) { os << "Value stored into '"; os << GetReceiverInterfaceName(msg) << "' cannot be nil"; } else { assert(Arg == 1); os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; } } else llvm_unreachable("Missing foundation class for the subscript expr"); } else { if (Class == FC_NSDictionary) { if (Arg == 0) os << "Value argument "; else { assert(Arg == 1); os << "Key argument "; } os << "to '" << msg.getSelector().getAsString() << "' cannot be nil"; } else { os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; } } BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(msg.getArgSourceRange(Arg)); bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R); C.emitReport(R); } }
void NilArgChecker::warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const { ProgramStateRef State = C.getState(); if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { if (ExplodedNode *N = C.generateSink()) { generateBugReport(N, Msg, E->getSourceRange(), E, C); } } }
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType ToTy = Ctx.getCanonicalType(CE->getType()); const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); if (!ToPTy) return; QualType ToPointeeTy = ToPTy->getPointeeType(); // Only perform the check if 'ToPointeeTy' is a complete type. if (ToPointeeTy->isIncompleteType()) return; ProgramStateRef state = C.getState(); const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion(); if (R == 0) return; const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); if (SR == 0) return; SValBuilder &svalBuilder = C.getSValBuilder(); SVal extent = SR->getExtent(svalBuilder); const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent); if (!extentInt) return; CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue()); CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); // Ignore void, and a few other un-sizeable types. if (typeSize.isZero()) return; if (regionSize % typeSize == 0) return; if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) return; if (ExplodedNode *errorNode = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug(this, "Cast region with wrong size.", "Cast a region whose size is not a multiple" " of the destination type size.")); BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); C.emitReport(R); } }
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { // If we're binding to a reference, check if the value is known to be null. if (V.isUndef()) return; const MemRegion *MR = L.getAsRegion(); const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); if (!TVR) return; if (!TVR->getValueType()->isReferenceType()) return; ProgramStateRef State = C.getState(); ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull) { if (!StNonNull) { reportBug(StNull, S, C, /*isBind=*/true); return; } // 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() }; dispatchEvent(event); } } // Unlike a regular null dereference, initializing a reference with a // dereferenced null pointer does not actually cause a runtime exception in // Clang's implementation of references. // // int &r = *p; // safe?? // if (p != NULL) return; // uh-oh // r = 5; // trap here // // The standard says this is invalid as soon as we try to create a "null // reference" (there is no such thing), but turning this into an assumption // that 'p' is never null will not match our actual runtime behavior. // So we do not record this assumption, allowing us to warn on the last line // of this example. // // We do need to add a transition because we may have generated a sink for // the "implicit" null dereference. C.addTransition(State, this); }
void DivZeroChecker::reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C) const { if (ExplodedNode *N = C.generateSink(StateZero)) { if (!BT) BT.reset(new BuiltinBug("Division by zero")); BugReport *R = new BugReport(*BT, Msg, N); bugreporter::trackNullOrUndefValue(N, bugreporter::GetDenomExpr(N), *R); C.emitReport(R); } }
void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, const ObjCAtSynchronizedStmt *S) { const Expr *Ex = S->getSynchExpr(); const GRState *state = C.getState(); SVal V = state->getSVal(Ex); // Uninitialized value used for the mutex? if (isa<UndefinedVal>(V)) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef = new BuiltinBug("Uninitialized value used as mutex " "for @synchronized"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); C.EmitReport(report); } return; } if (V.isUnknown()) return; // Check for null mutexes. const GRState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); if (nullState) { if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.generateNode(nullState)) { if (!BT_null) BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() " "(no synchronization will occur)"); EnhancedBugReport *report = new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); C.EmitReport(report); return; } } // Don't add a transition for 'nullState'. If the value is // under-constrained to be null or non-null, assume it is non-null // afterwards. } if (notNullState) C.addTransition(notNullState); }
void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const { const Expr *Ex = S->getSynchExpr(); ProgramStateRef state = C.getState(); SVal V = state->getSVal(Ex, C.getLocationContext()); // Uninitialized value used for the mutex? if (V.getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " "for @synchronized")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *report); C.emitReport(report); } return; } if (V.isUnknown()) return; // Check for null mutexes. ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>()); if (nullState) { if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.addTransition(nullState)) { if (!BT_null) BT_null.reset(new BuiltinBug( this, "Nil value used as mutex for @synchronized() " "(no synchronization will occur)")); BugReport *report = new BugReport(*BT_null, BT_null->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *report); C.emitReport(report); return; } } // Don't add a transition for 'nullState'. If the value is // under-constrained to be null or non-null, assume it is non-null // afterwards. } if (notNullState) C.addTransition(notNullState); }
static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, const Expr *TrackingE = 0) { ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *Report = new BugReport(BT, BT.getDescription(), N); Report->addRange(RetE->getSourceRange()); bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report); C.emitReport(Report); }
static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, const Expr *TrackingE = nullptr) { ExplodedNode *N = C.generateSink(); if (!N) return; auto Report = llvm::make_unique<BugReport>(BT, BT.getDescription(), N); Report->addRange(RetE->getSourceRange()); bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report); C.emitReport(std::move(Report)); }
void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const { if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Use destroyed lock", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; auto Report = llvm::make_unique<BugReport>( *BT_destroylock, "This lock has already been destroyed", N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); }
void UnixAPIChecker::ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg, SourceRange SR) const { ExplodedNode *N = C.generateSink(State); if (!N) return; LazyInitialize(BT_open, "Improper use of 'open'"); auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); Report->addRange(SR); C.emitReport(std::move(Report)); }