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 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); }
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.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); }
/// Emits a warning if the current context is -dealloc and DeallocedValue /// must not be directly dealloced in a -dealloc. Returns true if a diagnostic /// was emitted. bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, const ObjCMethodCall &M, CheckerContext &C) const { // Find the property backing the instance variable that M // is dealloc'ing. const ObjCPropertyImplDecl *PropImpl = findPropertyOnDeallocatingInstance(DeallocedValue, C); if (!PropImpl) return false; if (getDeallocReleaseRequirement(PropImpl) != ReleaseRequirement::MustRelease) { return false; } ExplodedNode *ErrNode = C.generateErrorNode(); if (!ErrNode) return false; std::string Buf; llvm::raw_string_ostream OS(Buf); OS << "'" << *PropImpl->getPropertyIvarDecl() << "' should be released rather than deallocated"; std::unique_ptr<BugReport> BR( new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode)); BR->addRange(M.getOriginExpr()->getSourceRange()); C.emitReport(std::move(BR)); return true; }
void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // Using a fixed address is not portable because that address will probably // not be valid in all environments or platforms. if (B->getOpcode() != BO_Assign) return; QualType T = B->getType(); if (!T->isPointerType()) return; SVal RV = C.getSVal(B->getRHS()); if (!RV.isConstant() || RV.isZeroConstant()) return; if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Use fixed address", "Using a fixed address is not portable because that " "address will probably not be valid in all " "environments or platforms.")); auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.emitReport(std::move(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; }
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.generateErrorNode(); if (N) { if (!BT_doubleclose) BT_doubleclose.reset(new BuiltinBug( this, "Double fclose", "Try to close a file Descriptor already" " closed. Cause undefined behaviour.")); C.emitReport(llvm::make_unique<BugReport>( *BT_doubleclose, BT_doubleclose->getDescription(), N)); } return nullptr; } // Close the File Descriptor. return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); }
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), state, C))) return; // Check the legality of the 'whence' argument of 'fseek'. SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); if (!CI) return; int64_t x = CI->getValue().getSExtValue(); if (x >= 0 && x <= 2) return; if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_illegalwhence) BT_illegalwhence.reset( new BuiltinBug(this, "Illegal whence argument", "The whence argument to fseek() should be " "SEEK_SET, SEEK_END, or SEEK_CUR.")); C.emitReport(llvm::make_unique<BugReport>( *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); } }
void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock, enum LockingSemantics semantics) const { const MemRegion *LockR = Lock.getAsRegion(); if (!LockR) return; ProgramStateRef State = C.getState(); const SymbolRef *sym = State->get<DestroyRetVal>(LockR); if (sym) State = resolvePossiblyDestroyedMutex(State, LockR, sym); const LockState *LState = State->get<LockMap>(LockR); // Checking the return value of the destroy method only in the case of // PthreadSemantics if (semantics == PthreadSemantics) { if (!LState || LState->isUnlocked()) { SymbolRef sym = C.getSVal(CE).getAsSymbol(); if (!sym) { State = State->remove<LockMap>(LockR); C.addTransition(State); return; } State = State->set<DestroyRetVal>(LockR, sym); if (LState && LState->isUnlocked()) State = State->set<LockMap>( LockR, LockState::getUnlockedAndPossiblyDestroyed()); else State = State->set<LockMap>( LockR, LockState::getUntouchedAndPossiblyDestroyed()); C.addTransition(State); return; } } else { 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.generateErrorNode(); 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 UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); if (state->getSVal(B, LCtx).isUndef()) { // 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; // Generate an error node. ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT) BT.reset( new BuiltinBug(this, "Result of operation is garbage or undefined")); SmallString<256> sbuf; 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; } else if (state->getSVal(B->getRHS(), LCtx).isUndef()) { Ex = B->getRHS()->IgnoreParenCasts(); isLeft = false; } if (Ex) { OS << "The " << (isLeft ? "left" : "right") << " operand of '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' is a garbage value"; } else { // Neither operand was undefined, but the result is undefined. OS << "The result of the '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' expression is undefined"; } auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); bugreporter::trackNullOrUndefValue(N, Ex, *report); } else bugreporter::trackNullOrUndefValue(N, B, *report); C.emitReport(std::move(report)); } }
// Check the jail state before any function call except chroot and chdir(). void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; ASTContext &Ctx = C.getASTContext(); if (!II_chroot) II_chroot = &Ctx.Idents.get("chroot"); if (!II_chdir) II_chdir = &Ctx.Idents.get("chdir"); // 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 (!BT_BreakJail) BT_BreakJail.reset(new BuiltinBug("Break out of jail", "No call of chdir(\"/\") immediately " "after chroot")); BugReport *R = new BugReport(*BT_BreakJail, BT_BreakJail->getDescription(), N); C.emitReport(R); } return; }
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), state, C))) return; // Check the legality of the 'whence' argument of 'fseek'. SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); if (!CI) return; int64_t x = CI->getValue().getSExtValue(); if (x >= 0 && x <= 2) return; if (ExplodedNode *N = C.addTransition(state)) { if (!BT_illegalwhence) BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument", "The whence argument to fseek() should be " "SEEK_SET, SEEK_END, or SEEK_CUR.")); BugReport *R = new BugReport(*BT_illegalwhence, BT_illegalwhence->getDescription(), N); C.emitReport(R); } }
void 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.generateErrorNode(); if (!N) return; if (!BT) BT.reset(new BuiltinBug(this, "Array subscript is undefined")); // Generate a report for this bug. auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); bugreporter::trackExpressionValue(N, A->getIdx(), *R); C.emitReport(std::move(R)); }
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 NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!msg.isInstanceMessage()) return; const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); if (!OD) return; if (!OD->getIdentifier()->isStr("NSAutoreleasePool")) return; if (releaseS.isNull()) releaseS = GetNullarySelector("release", C.getASTContext()); // Sending 'release' message? if (msg.getSelector() != releaseS) return; if (!BT) BT.reset(new BugType("Use -drain instead of -release", "API Upgrade (Apple)")); ExplodedNode *N = C.addTransition(); if (!N) { assert(0); return; } BugReport *Report = new BugReport(*BT, "Use -drain instead of -release when " "using NSAutoreleasePool and garbage collection", N); Report->addRange(msg.getSourceRange()); C.emitReport(Report); }
void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // Using a fixed address is not portable because that address will probably // not be valid in all environments or platforms. if (B->getOpcode() != BO_Assign) return; QualType T = B->getType(); if (!T->isPointerType()) return; ProgramStateRef state = C.getState(); SVal RV = state->getSVal(B->getRHS(), C.getLocationContext()); if (!RV.isConstant() || RV.isZeroConstant()) return; if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset( new BuiltinBug(this, "Use fixed address", "Using a fixed address is not portable because that " "address will probably not be valid in all " "environments or platforms.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.emitReport(R); } }
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 IteratorChecker::reportOutOfRangeBug(const StringRef &Message, const SVal &Val, CheckerContext &C, ExplodedNode *ErrNode) const { auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode); R->markInteresting(Val); C.emitReport(std::move(R)); }
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); } } } }
void PointerArithChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) return; ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); SVal LV = state->getSVal(B->getLHS(), LCtx); SVal RV = state->getSVal(B->getRHS(), LCtx); const MemRegion *LR = LV.getAsRegion(); if (!LR || !RV.isConstant()) return; // If pointer arithmetic is done on variables of non-array type, this often // means behavior rely on memory organization, which is dangerous. if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || isa<CompoundLiteralRegion>(LR)) { if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset(new BuiltinBug("Dangerous pointer arithmetic", "Pointer arithmetic done on non-array variables " "means reliance on memory layout, which is " "dangerous.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.emitReport(R); } } }
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const { assert(E); // Check for taint. ProgramStateRef State = C.getState(); Optional<SVal> PointedToSVal = getPointedToSVal(C, E); SVal TaintedSVal; if (PointedToSVal && State->isTainted(*PointedToSVal)) TaintedSVal = *PointedToSVal; else if (State->isTainted(E, C.getLocationContext())) TaintedSVal = C.getSVal(E); else return false; // Generate diagnostic. if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, Msg, N); report->addRange(E->getSourceRange()); report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal)); C.emitReport(std::move(report)); return true; } return false; }
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, ExplodedNode *N) const { if (!BT_msg_ret) BT_msg_ret.reset( new BuiltinBug(this, "Receiver in message expression is 'nil'")); const ObjCMessageExpr *ME = msg.getOriginExpr(); QualType ResTy = msg.getResultType(); SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '"; ME->getSelector().print(os); os << "' is nil"; if (ResTy->isReferenceType()) { os << ", which results in forming a null reference"; } else { os << " and returns a value of type '"; msg.getResultType().print(os, C.getLangOpts()); os << "' that will be garbage"; } auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N); report->addRange(ME->getReceiverRange()); // FIXME: This won't track "self" in messages to super. if (const Expr *receiver = ME->getInstanceReceiver()) { bugreporter::trackNullOrUndefValue(N, receiver, *report); } C.emitReport(std::move(report)); }
void BoolAssignmentChecker::emitReport(ProgramStateRef state, CheckerContext &C) const { if (ExplodedNode *N = C.addTransition(state)) { if (!BT) BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value")); C.emitReport(new BugReport(*BT, BT->getDescription(), N)); } }
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 ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const { if (!BT) BT.reset( new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); // Generate a report for this bug. auto R = llvm::make_unique<BugReport>(*BT, Msg, N); C.emitReport(std::move(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()) bugreporter::trackNullOrUndefValue(N, ReceiverE, *R); C.emitReport(R); } return; } else { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = recVal.castAs<DefinedOrUnknownSVal>(); 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 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 ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, CheckerContext &C) const { // Check for out of bound array element access. const MemRegion *R = l.getAsRegion(); if (!R) return; const ElementRegion *ER = dyn_cast<ElementRegion>(R); if (!ER) return; // Get the index of the accessed element. DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); // Zero index is always in bound, this also passes ElementRegions created for // pointer casts. if (Idx.isZeroConstant()) return; ProgramStateRef state = C.getState(); // Get the size of the array. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; if (!BT) BT.reset(new BuiltinBug( this, "Out-of-bound array access", "Access out-of-bound array element (buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); report->addRange(LoadS->getSourceRange()); C.emitReport(std::move(report)); return; } // Array bound check succeeded. From this point forward the array bound // should always succeed. C.addTransition(StInBound); }
void 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 CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType ToTy = Ctx.getCanonicalType(CE->getType()); const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); if (!ToPTy) return; QualType ToPointeeTy = ToPTy->getPointeeType(); // Only perform the check if 'ToPointeeTy' is a complete type. if (ToPointeeTy->isIncompleteType()) return; ProgramStateRef state = C.getState(); const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion(); if (!R) return; const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); if (!SR) return; SValBuilder &svalBuilder = C.getSValBuilder(); SVal extent = SR->getExtent(svalBuilder); const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent); if (!extentInt) return; CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue()); CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); // Ignore void, and a few other un-sizeable types. if (typeSize.isZero()) return; if (regionSize % typeSize == 0) return; if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) return; if (ExplodedNode *errorNode = C.generateErrorNode()) { if (!BT) BT.reset(new BuiltinBug(this, "Cast region with wrong size.", "Cast a region whose size is not a multiple" " of the destination type size.")); auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); C.emitReport(std::move(R)); } }