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'"); BugReport *Report = new BugReport(*BT_open, Msg, N); Report->addRange(SR); C.emitReport(Report); }
void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const { ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT_returnstack) BT_returnstack.reset( new BuiltinBug("Return of address to stack-allocated memory")); // Generate a report for this bug. SmallString<512> buf; llvm::raw_svector_ostream os(buf); SourceRange range = genName(os, R, C.getASTContext()); os << " returned to caller"; BugReport *report = new BugReport(*BT_returnstack, os.str(), N); report->addRange(RetE->getSourceRange()); if (range.isValid()) report->addRange(range); C.emitReport(report); }
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) 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 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; BugReport *Report = new BugReport(*BT_destroylock, "This lock has already been destroyed", N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(Report); }
void UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, CheckerContext &C) const { if (!BE->getBlockDecl()->hasCaptures()) return; ProgramStateRef state = C.getState(); const BlockDataRegion *R = cast<BlockDataRegion>(state->getSVal(BE, C.getLocationContext()).getAsRegion()); BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), E = R->referenced_vars_end(); for (; I != E; ++I) { // This VarRegion is the region associated with the block; we need // the one associated with the encompassing context. const VarRegion *VR = I.getCapturedRegion(); const VarDecl *VD = VR->getDecl(); if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) continue; // 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 (!BT) BT.reset( new BuiltinBug(this, "uninitialized variable captured by block")); // Generate a bug report. SmallString<128> buf; llvm::raw_svector_ostream os(buf); os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; BugReport *R = new BugReport(*BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( *V, VR, /*EnableNullFPSuppression*/ false)); R->disablePathPruning(); // need location of block C.emitReport(R); } } } }
// Handle operator calls. First, if it is operator=, check the argument, // and handle assigning and set target state appropriately. Otherwise, for // other operators, check the args for bad iterators and handle comparisons. void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, CheckerContext &C) const { const LocationContext *LC = C.getLocationContext(); const ProgramState *state = C.getState(); OverloadedOperatorKind Kind = OCE->getOperator(); if (Kind == OO_Equal) { checkExpr(C, OCE->getArg(1)); state = handleAssign(state, OCE->getArg(0), OCE->getArg(1), LC); C.addTransition(state); return; } else { checkArgs(C, OCE); // If it is a compare and both are iterators, ensure that they are for // the same container. if (Kind == OO_EqualEqual || Kind == OO_ExclaimEqual || Kind == OO_Less || Kind == OO_LessEqual || Kind == OO_Greater || Kind == OO_GreaterEqual) { const MemRegion *MR0, *MR1; MR0 = getRegion(state, OCE->getArg(0), LC); if (!MR0) return; MR1 = getRegion(state, OCE->getArg(1), LC); if (!MR1) return; const RefState *RS0, *RS1; RS0 = state->get<IteratorState>(MR0); if (!RS0) return; RS1 = state->get<IteratorState>(MR1); if (!RS1) return; if (RS0->getMemRegion() != RS1->getMemRegion()) { if (ExplodedNode *N = C.addTransition()) { if (!BT_Incompatible) const_cast<IteratorsChecker*>(this)->BT_Incompatible = new BuiltinBug( "Cannot compare iterators from different containers"); BugReport *R = new BugReport(*BT_Incompatible, BT_Incompatible->getDescription(), N); R->addRange(OCE->getSourceRange()); C.EmitReport(R); } } } } }
void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { const ProgramState *state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); if (state->getSVal(B, LCtx).isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Result of operation is garbage or undefined")); llvm::SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = NULL; bool isLeft = true; if (state->getSVal(B->getLHS(), 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"; } BugReport *report = new BugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); } else report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B)); C.EmitReport(report); } }
void UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, CheckerContext &C) const { if (!BE->getBlockDecl()->hasCaptures()) return; ProgramStateRef state = C.getState(); const BlockDataRegion *R = cast<BlockDataRegion>(state->getSVal(BE, C.getLocationContext()).getAsRegion()); BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), E = R->referenced_vars_end(); for (; I != E; ++I) { // This VarRegion is the region associated with the block; we need // the one associated with the encompassing context. const VarRegion *VR = *I; const VarDecl *VD = VR->getDecl(); if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) continue; // Get the VarRegion associated with VD in the local stack frame. const LocationContext *LC = C.getLocationContext(); VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); SVal VRVal = state->getSVal(VR); if (VRVal.isUndef()) if (ExplodedNode *N = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("uninitialized variable captured by block")); // Generate a bug report. SmallString<128> buf; llvm::raw_svector_ostream os(buf); os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; BugReport *R = new BugReport(*BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR)); // need location of block C.EmitReport(R); } } }
BugReport *MacOSKeychainAPIChecker:: generateAllocatedDataNotReleasedReport(const AllocationPair &AP, ExplodedNode *N) const { const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; initBugType(); llvm::SmallString<70> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Allocated data is not released: missing a call to '" << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(AP.first)); Report->addRange(SourceRange()); return Report; }
void TaintTesterChecker::checkPostStmt(const Expr *E, CheckerContext &C) const { ProgramStateRef State = C.getState(); if (!State) return; if (State->isTainted(E, C.getLocationContext())) { if (ExplodedNode *N = C.addTransition()) { initBugType(); BugReport *report = new BugReport(*BT, "tainted",N); report->addRange(E->getSourceRange()); C.emitReport(report); } } }
void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE) { ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *R = new BugReport(*BT, BT->getName(), N); if (BadE) { R->addRange(BadE->getSourceRange()); if (BadE->isGLValue()) BadE = bugreporter::getDerefExpr(BadE); bugreporter::trackNullOrUndefValue(N, BadE, *R); } C.emitReport(R); }
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const { SVal X = Ctx.getState()->getSVal(Condition, Ctx.getLocationContext()); if (X.isUndef()) { // Generate a sink node, which implicitly marks both outgoing branches as // infeasible. ExplodedNode *N = Ctx.generateSink(); if (N) { if (!BT) BT.reset(new BuiltinBug( this, "Branch condition evaluates to a garbage value")); // What's going on here: we want to highlight the subexpression of the // condition that is the most likely source of the "uninitialized // branch condition." We do a recursive walk of the condition's // subexpressions and roughly look for the most nested subexpression // that binds to Undefined. We then highlight that expression's range. // Get the predecessor node and check if is a PostStmt with the Stmt // being the terminator condition. We want to inspect the state // of that node instead because it will contain main information about // the subexpressions. // Note: any predecessor will do. They should have identical state, // since all the BlockEdge did was act as an error sink since the value // had to already be undefined. assert (!N->pred_empty()); const Expr *Ex = cast<Expr>(Condition); ExplodedNode *PrevN = *N->pred_begin(); ProgramPoint P = PrevN->getLocation(); ProgramStateRef St = N->getState(); if (Optional<PostStmt> PS = P.getAs<PostStmt>()) if (PS->getStmt() == Ex) St = PrevN->getState(); FindUndefExpr FindIt(St, Ctx.getLocationContext()); Ex = FindIt.FindExpr(Ex); // Emit the bug report. BugReport *R = new BugReport(*BT, BT->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *R); R->addRange(Ex->getSourceRange()); Ctx.emitReport(R); } } }
void UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const { if (C.getState()->getSVal(A->getIdx(), C.getLocationContext()).isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("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 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(); // If we've already reached this node on another path, return. if (!ErrNode) return; // Generate the report. BugReport *R = new BugReport(*DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(FileDescSym); C.emitReport(R); }
void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, StringRef FName) const { if (CE->getNumArgs() < 1) return; // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. ProgramStateRef state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; ExplodedNode *N = C.generateSink(state); if (!N) return; if (!BT_dispatchOnce) BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", "Mac OS X API")); // Handle _dispatch_once. In some versions of the OS X SDK we have the case // that dispatch_once is a macro that wraps a call to _dispatch_once. // _dispatch_once is then a function which then calls the real dispatch_once. // Users do not care; they just want the warning at the top-level call. if (CE->getLocStart().isMacroID()) { StringRef TrimmedFName = FName.ltrim("_"); if (TrimmedFName != FName) FName = TrimmedFName; } SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << FName << "' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) os << " the local variable '" << VR->getDecl()->getName() << '\''; else os << " stack allocated memory"; os << " for the predicate value. Using such transient memory for " "the predicate is potentially dangerous."; if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(report); }
void DanglingDelegateChecker::verifyIvarDynamicStateAgainstStaticFacts(const Expr &expr, const ObjCIvarDecl *ivarDecl, CheckerContext &context) const { // first retrieve the dangerous 'static' facts we know about the ivar if (!ivarDecl) { return; } const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts || facts->_ivarFactsMap.find(ivarDecl) == facts->_ivarFactsMap.end()) { // not an interesting ivar (no entry) return; } const IvarFacts &ivarFacts = facts->_ivarFactsMap.at(ivarDecl); std::string ivarName = ivarDecl->getNameAsString(); // second retrieve the current 'dynamic' state of the ivar ProgramStateRef state = context.getState(); const IvarDynamicState emptyIds; const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl); if (!ids) { ids = &emptyIds; } // Verify that all the dangerous properties have been cleared const StringSet &dangerousProperties = ivarFacts._mayStoreSelfInUnsafeProperty; const StringSet &clearedProperties = ids->_assignPropertyWasCleared; const std::function<void(StringRef)> emitBug([this, &context, &expr](StringRef str) { BugReport *report = new BugReport(*_bugType, str, context.getPredecessor()); report->addRange(expr.getSourceRange()); context.emitReport(report); }); verifyAndReportDangerousProperties(dangerousProperties, clearedProperties, ivarName, ivarDecl->getType(), "", emitBug); // Verify that the object in the ivar is not 'observing' self (TODO) // if (ivarFacts._mayObserveSelf && !ids->_observerWasCleared) { // } // Verify that the object in the ivar is not 'targeting' self (TODO) // if (ivarFacts._mayTargetSelf && !ids->_targetWasCleared) { // } }
void NilArgChecker::WarnNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned int Arg) const { if (!BT) BT.reset(new APIMisuse("nil argument")); if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); 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)); C.emitReport(R); } }
void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { StringRef Name = C.getCalleeName(CE); if (Name.empty() || CE->getNumArgs() < 2) return; // Check the array access. if (Name.equals("CFArrayGetValueAtIndex")) { ProgramStateRef State = C.getState(); // Retrieve the size. // Find out if we saw this array symbol before and have information about it. const Expr *ArrayExpr = CE->getArg(0); SymbolRef ArraySym = getArraySym(ArrayExpr, C); if (!ArraySym) return; const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); if (!Size) return; // Get the index. const Expr *IdxExpr = CE->getArg(1); SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); if (IdxVal.isUnknownOrUndef()) return; DefinedSVal Idx = cast<DefinedSVal>(IdxVal); // 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); if (!N) return; initBugType(); BugReport *R = new BugReport(*BT, "Index is out of bounds", N); R->addRange(IdxExpr->getSourceRange()); C.EmitReport(R); return; } } }
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()) { if (!BT_msg_undef) BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is " "an uninitialized value")); BugReport *R = new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N); R->addRange(receiver->getSourceRange()); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver)); 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); }
// FIXME: Eventually this should be rolled into the MallocChecker, but this // check is more basic and is valuable for widespread use. void UnixAPIChecker::CheckMallocZero(CheckerContext &C, const CallExpr *CE) const { // Sanity check that malloc takes one argument. if (CE->getNumArgs() != 1) return; // Check if the allocation size is 0. const ProgramState *state = C.getState(); SVal argVal = state->getSVal(CE->getArg(0)); if (argVal.isUnknownOrUndef()) return; const ProgramState *trueState, *falseState; llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); // Is the value perfectly constrained to zero? if (falseState && !trueState) { ExplodedNode *N = C.generateSink(falseState); if (!N) return; // FIXME: Add reference to CERT advisory, and/or C99 standard in bug // output. LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); BugReport *report = new BugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" " size of 0 bytes", N); report->addRange(CE->getArg(0)->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, CE->getArg(0))); C.EmitReport(report); return; } // Assume the the value is non-zero going forward. assert(trueState); if (trueState != state) { C.addTransition(trueState); } }
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 << "Dictionary object cannot be nil"; else { assert(Arg == 1); os << "Dictionary key cannot be nil"; } } else llvm_unreachable("Missing foundation class for the subscript expr"); } 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 DanglingDelegateChecker::checkEndFunction(CheckerContext &context) const { // dealloc implicitly releases ivars only in ARC-mode if (!context.getLangOpts().ObjCAutoRefCount) { return; } const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts) { return; } const ObjCMethodDecl *decl = dyn_cast_or_null<ObjCMethodDecl>(context.getStackFrame()->getDecl()); if (!decl || decl->getSelector().getAsString() != "dealloc") { return; } const std::function<void(StringRef)> emitBug([this, decl, &context](StringRef str) { BugReport *report = new BugReport(*_bugType, str, context.getPredecessor()); report->addRange(decl->getSourceRange()); context.emitReport(report); }); ProgramStateRef state = context.getState(); // Verify that all the dangerous properties have been cleared const IvarDynamicState emptyIds; for (auto it = facts->_ivarFactsMap.begin(), end = facts->_ivarFactsMap.end(); it != end; it++) { const ObjCIvarDecl *ivarDecl = it->first; const StringSet &dangerousProperties = it->second._mayStoreSelfInUnsafeProperty; const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl); if (!ids) { ids = &emptyIds; } const StringSet &clearedProperties = ids->_assignPropertyWasCleared; verifyAndReportDangerousProperties(dangerousProperties, clearedProperties, ivarDecl->getNameAsString(), ivarDecl->getType(), "ARC-generated code", emitBug); } }
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const { assert(E); // Check for taint. ProgramStateRef State = C.getState(); if (!State->isTainted(getPointedToSymbol(C, E)) && !State->isTainted(E, C.getLocationContext())) return false; // Generate diagnostic. if (ExplodedNode *N = C.addTransition()) { initBugType(); BugReport *report = new BugReport(*BT, Msg, N); report->addRange(E->getSourceRange()); C.emitReport(report); return true; } return false; }
void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { const Expr *RetE = RS->getRetValue(); if (!RetE) return; if (!C.getState()->getSVal(RetE, C.getLocationContext()).isUndef()) return; // "return;" is modeled to evaluate to an UndefinedValue. Allow UndefinedValue // to be returned in functions returning void to support the following pattern: // void foo() { // return; // } // void test() { // return foo(); // } const StackFrameContext *SFC = C.getStackFrame(); QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl()); if (!RT.isNull() && RT->isSpecificBuiltinType(BuiltinType::Void)) return; ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Garbage return value", "Undefined or garbage value returned to caller")); BugReport *report = new BugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); bugreporter::trackNullOrUndefValue(N, RetE, *report); C.emitReport(report); }
// Generates an error report, indicating that the function whose name is given // will perform a zero byte allocation. // Returns false if an error occured, true otherwise. bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, const ProgramState *falseState, const Expr *arg, const char *fn_name) const { ExplodedNode *N = C.generateSink(falseState); if (!N) return false; LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); report->addRange(arg->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg)); C.EmitReport(report); return true; }
void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const { // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. // They can possibly be refactored. if (CE->getNumArgs() < 1) return; // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. const ProgramState *state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; ExplodedNode *N = C.generateSink(state); if (!N) return; llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to 'pthread_once' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) os << " the local variable '" << VR->getDecl()->getName() << '\''; else os << " stack allocated memory"; os << " for the \"control\" value. Using such transient memory for " "the control value is potentially dangerous."; if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); }
void TypeConfusionChecker::reportBug(CheckerContext &C, SourceRange SR, StringRef Message, StringRef declName) const { const char *name = "Type confusion checker"; const char *desc = "Flags unsafe casts"; ExplodedNode *EN = C.generateSink(); if (!EN) return; if (!BT) BT.reset(new BuiltinBug(this, name, desc)); BugReport *R = new BugReport(*BT, Message, EN); R->addRange(SR); Diag.encodeBugInfo(declName, C); for (auto &i : Diag.getBugInfoDiag()) { R->addExtraText(i); } C.emitReport(R); }
void PointerSubChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // When doing pointer subtraction, if the two pointers do not point to the // same memory chunk, emit a warning. if (B->getOpcode() != BO_Sub) return; 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(); const MemRegion *RR = RV.getAsRegion(); if (!(LR && RR)) return; const MemRegion *BaseLR = LR->getBaseRegion(); const MemRegion *BaseRR = RR->getBaseRegion(); if (BaseLR == BaseRR) return; // Allow arithmetic on different symbolic regions. if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) return; if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset( new BuiltinBug(this, "Pointer subtraction", "Subtraction of two pointers that do not point to " "the same memory chunk may cause incorrect result.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.emitReport(R); } }
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")); ASTContext &Ctx = C.getASTContext(); releaseS = GetNullarySelector("release", Ctx); retainS = GetNullarySelector("retain", Ctx); autoreleaseS = GetNullarySelector("autorelease", Ctx); drainS = GetNullarySelector("drain", Ctx); } if (msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); assert(Class); Selector S = msg.getSelector(); if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; if (ExplodedNode *N = C.addTransition()) { SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The '"; S.print(os); os << "' message should be sent to instances " "of class '" << Class->getName() << "' and not the class directly"; BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(msg.getSourceRange()); C.emitReport(report); } }
void CastToStructChecker::checkPreStmt(const CastExpr *CE, CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType OrigTy = Ctx.getCanonicalType(E->getType()); QualType ToTy = Ctx.getCanonicalType(CE->getType()); const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); if (!ToPTy || !OrigPTy) return; QualType OrigPointeeTy = OrigPTy->getPointeeType(); QualType ToPointeeTy = ToPTy->getPointeeType(); if (!ToPointeeTy->isStructureOrClassType()) return; // We allow cast from void*. if (OrigPointeeTy->isVoidType()) return; // Now the cast-to-type is struct pointer, the original type is not void*. if (!OrigPointeeTy->isRecordType()) { if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset( new BuiltinBug(this, "Cast from non-struct type to struct type", "Casting a non-structure type to a structure type " "and accessing a field can lead to memory access " "errors or data corruption.")); BugReport *R = new BugReport(*BT,BT->getDescription(), N); R->addRange(CE->getSourceRange()); C.emitReport(R); } } }