void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, const Stmt *S, CheckerContext &C) const { if (!isLoad) return; if (loc.isUndef() || !loc.getAs<Loc>()) return; ASTContext &Ctx = C.getASTContext(); ProgramStateRef state = C.getState(); // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting // SVal so that we can later check it when handling the // ImplicitNullDerefEvent event. // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of // function ? QualType parmT = parameterTypeFromSVal(loc, C); if (parmT.isNull()) return; if (!NSErrorII) NSErrorII = &Ctx.Idents.get("NSError"); if (!CFErrorII) CFErrorII = &Ctx.Idents.get("CFErrorRef"); if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) { setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C); return; } if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) { setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C); return; } }
void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // FIXME: This sort of logic is common to several checkers, including // UnixAPIChecker, PthreadLockChecker, and CStringChecker. Should refactor. const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); if (!Fn) return; const IdentifierInfo *FI = Fn->getIdentifier(); if (!FI) return; SubChecker SC = llvm::StringSwitch<SubChecker>(FI->getName()) .Cases("dispatch_once", "dispatch_once_f", &MacOSXAPIChecker::CheckDispatchOnce) .Default(NULL); if (SC) (this->*SC)(C, CE, FI); }
// Report deallocator mismatch. Remove the region from tracking - reporting a // missing free error after this one is redundant. void MacOSKeychainAPIChecker:: generateDeallocatorMismatchReport(const AllocationPair &AP, const Expr *ArgExpr, CheckerContext &C) const { ProgramStateRef State = C.getState(); State = State->remove<AllocatedData>(AP.first); ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); SmallString<80> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int PDeallocIdx = FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; os << "Deallocator doesn't match the allocator: '" << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); Report->addRange(ArgExpr->getSourceRange()); markInteresting(Report.get(), AP); C.emitReport(std::move(Report)); }
void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, const IdentifierInfo *FI) 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. const ProgramState *state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; ExplodedNode *N = C.generateSink(state); if (!N) return; if (!BT_dispatchOnce) BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", "Mac OS X API")); llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << FI->getName() << "' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) os << " the local variable '" << VR->getDecl()->getName() << '\''; else os << " stack allocated memory"; os << " for the predicate value. Using such transient memory for " "the predicate is potentially dangerous."; if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); }
void MPIChecker::allRegionsUsedByWait( llvm::SmallVector<const MemRegion *, 2> &ReqRegions, const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { MemRegionManager *const RegionManager = MR->getMemRegionManager(); if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { const MemRegion *SuperRegion{nullptr}; if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { SuperRegion = ER->getSuperRegion(); } // A single request is passed to MPI_Waitall. if (!SuperRegion) { ReqRegions.push_back(MR); return; } const auto &Size = Ctx.getStoreManager().getSizeInElements( Ctx.getState(), SuperRegion, CE.getArgExpr(1)->getType()->getPointeeType()); const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); for (size_t i = 0; i < ArrSize; ++i) { const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); const ElementRegion *const ER = RegionManager->getElementRegion( CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, Ctx.getASTContext()); ReqRegions.push_back(ER->getAs<MemRegion>()); } } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { ReqRegions.push_back(MR); } }
void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, CheckerContext &Ctx) const { if (!SymReaper.hasDeadSymbols()) return; ProgramStateRef State = Ctx.getState(); const auto &Requests = State->get<RequestMap>(); if (Requests.isEmpty()) return; static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); ExplodedNode *ErrorNode{nullptr}; auto ReqMap = State->get<RequestMap>(); for (const auto &Req : ReqMap) { if (!SymReaper.isLiveRegion(Req.first)) { if (Req.second.CurrentState == Request::State::Nonblocking) { if (!ErrorNode) { ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); State = ErrorNode->getState(); } BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, Ctx.getBugReporter()); } State = State->remove<RequestMap>(Req.first); } } // Transition to update the state regarding removed requests. if (!ErrorNode) { Ctx.addTransition(State); } else { Ctx.addTransition(State, ErrorNode); } }
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); if (!SS) continue; if (SS->isOpened()) { ExplodedNode *N = C.generateErrorNode(); if (N) { if (!BT_ResourceLeak) BT_ResourceLeak.reset(new BuiltinBug( this, "Resource Leak", "Opened File never closed. Potential Resource leak.")); C.emitReport(llvm::make_unique<BugReport>( *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); } } } }
// Need to handle DeclStmts to pick up initializing of iterators and to mark // uninitialized ones as Undefined. void IteratorsChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { const Decl *D = *DS->decl_begin(); const VarDecl *VD = dyn_cast<VarDecl>(D); // Only care about iterators. if (getTemplateKind(VD->getType()) != VectorIteratorKind) return; // Get the MemRegion associated with the iterator and mark it as Undefined. const ProgramState *state = C.getState(); Loc VarLoc = state->getLValue(VD, C.getLocationContext()); const MemRegion *MR = VarLoc.getAsRegion(); if (!MR) return; state = state->set<IteratorState>(MR, RefState::getUndefined()); // if there is an initializer, handle marking Valid if a proper initializer const Expr *InitEx = VD->getInit(); if (InitEx) { // FIXME: This is too syntactic. Since 'InitEx' will be analyzed first // it should resolve to an SVal that we can check for validity // *semantically* instead of walking through the AST. if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(InitEx)) { if (CE->getNumArgs() == 1) { const Expr *E = CE->getArg(0); if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E)) E = M->GetTemporaryExpr(); if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) InitEx = ICE->getSubExpr(); state = handleAssign(state, MR, InitEx, C.getLocationContext()); } } } C.addTransition(state); }
/// 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); }
void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { const ProgramState *State = C.getState(); AllocatedSetTy ASet = State->get<AllocatedData>(); if (ASet.isEmpty()) return; bool Changed = false; AllocationPairVec Errors; for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { if (SR.isLive(I->first)) continue; Changed = true; State = State->remove<AllocatedData>(I->first); // If the allocated symbol is null or if the allocation call might have // returned an error, do not report. if (State->getSymVal(I->first) || definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) continue; Errors.push_back(std::make_pair(I->first, &I->second)); } if (!Changed) return; // Generate the new, cleaned up state. ExplodedNode *N = C.addTransition(State); if (!N) return; // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); } }
void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; }
void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { const GRState *state = CheckDoubleClose(CE, C.getState(), C); if (state) C.addTransition(state); }
void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; ASTContext &Ctx = C.getASTContext(); if (!II) II = &Ctx.Idents.get("CFNumberCreate"); if (FD->getIdentifier() != II || CE->getNumArgs() != 3) return; // Get the value of the "theType" argument. const LocationContext *LCtx = C.getLocationContext(); SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!OptTargetSize) return; uint64_t TargetSize = *OptTargetSize; // Look at the value of the integer being passed by reference. Essentially // we want to catch cases where the value passed in is not equal to the // size of the type being created. SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); if (!LV) return; const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); if (!R) return; QualType T = Ctx.getCanonicalType(R->getValueType()); // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. if (!T->isIntegralOrEnumerationType()) return; uint64_t SourceSize = Ctx.getTypeSize(T); // CHECK: is SourceSize == TargetSize if (SourceSize == TargetSize) return; // 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. // 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." ; else os << (SourceSize - TargetSize) << " bits of the input integer will be lost."; if (!BT) BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate")); auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.emitReport(std::move(report)); } }
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, const char *BT_desc, llvm::OwningPtr<BugType> &BT) { if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); // Generate a report for this bug. BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(argRange); if (argEx) R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx)); C.EmitReport(R); } return true; } if (const nonloc::LazyCompoundVal *LV = dyn_cast<nonloc::LazyCompoundVal>(&V)) { class FindUninitializedField { public: SmallVector<const FieldDecl *, 10> FieldChain; private: ASTContext &C; StoreManager &StoreMgr; MemRegionManager &MrMgr; Store store; public: FindUninitializedField(ASTContext &c, StoreManager &storeMgr, MemRegionManager &mrMgr, Store s) : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} bool Find(const TypedValueRegion *R) { QualType T = R->getValueType(); if (const RecordType *RT = T->getAsStructureType()) { const RecordDecl *RD = RT->getDecl()->getDefinition(); assert(RD && "Referred record has no definition"); for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I!=E; ++I) { const FieldRegion *FR = MrMgr.getFieldRegion(*I, R); FieldChain.push_back(*I); T = (*I)->getType(); if (T->getAsStructureType()) { if (Find(FR)) return true; } else { const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; } FieldChain.pop_back(); } } return false; } }; const LazyCompoundValData *D = LV->getCVData(); FindUninitializedField F(C.getASTContext(), C.getState()->getStateManager().getStoreManager(), C.getSValBuilder().getRegionManager(), D->getStore()); if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); llvm::SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; if (F.FieldChain.size() == 1) os << " (e.g., field: '" << *F.FieldChain[0] << "')"; else { os << " (e.g., via the field chain: '"; bool first = true; for (SmallVectorImpl<const FieldDecl *>::iterator DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ if (first) first = false; else os << '.'; os << **DI; } os << "')"; } // Generate a report for this bug. BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(argRange); // FIXME: enhance track back for uninitialized value for arbitrary // memregions C.EmitReport(R); } return true; } } return false; }
void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const Expr* Callee = CE->getCallee(); const GRState *state = C.getState(); SVal CallV = state->getSVal(Callee); const FunctionDecl* FD = CallV.getAsFunctionDecl(); if (!FD) return; ASTContext &Ctx = C.getASTContext(); if (!II) II = &Ctx.Idents.get("CFNumberCreate"); if (FD->getIdentifier() != II || CE->getNumArgs() != 3) return; // Get the value of the "theType" argument. SVal TheTypeVal = state->getSVal(CE->getArg(1)); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!TargetSize.isKnown()) return; // Look at the value of the integer being passed by reference. Essentially // we want to catch cases where the value passed in is not equal to the // size of the type being created. SVal TheValueExpr = state->getSVal(CE->getArg(2)); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); if (!LV) return; const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); if (!R) return; QualType T = Ctx.getCanonicalType(R->getValueType()); // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. if (!T->isIntegerType()) return; uint64_t SourceSize = Ctx.getTypeSize(T); // CHECK: is SourceSize == TargetSize if (SourceSize == TargetSize) return; // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; // otherwise generate a regular node. // // FIXME: We can actually create an abstract "CFNumber" object that has // the bits initialized to the provided values. // if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() : C.generateNode()) { llvm::SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << (SourceSize == 8 ? "An " : "A ") << SourceSize << " bit integer is used to initialize a CFNumber " "object that represents " << (TargetSize == 8 ? "an " : "a ") << TargetSize << " bit integer. "; if (SourceSize < TargetSize) os << (TargetSize - SourceSize) << " bits of the CFNumber value will be garbage." ; else os << (SourceSize - TargetSize) << " bits of the input integer will be lost."; if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.EmitReport(report); } }
void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); if (CE->getNumArgs() < 2) { // The frontend should issue a warning for this case, so this is a sanity // check. return; } else if (CE->getNumArgs() == 3) { const Expr *Arg = CE->getArg(2); QualType QT = Arg->getType(); if (!QT->isIntegerType()) { ReportOpenBug(C, state, "Third argument to 'open' is not an integer", Arg->getSourceRange()); return; } } else if (CE->getNumArgs() > 3) { ReportOpenBug(C, state, "Call to 'open' with more than three arguments", CE->getArg(3)->getSourceRange()); return; } // 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() == llvm::Triple::Apple) Val_O_CREAT = 0x0200; else { // FIXME: We need a more general way of getting the O_CREAT value. // We could possibly grovel through the preprocessor state, but // that would require passing the Preprocessor object to the ExprEngine. // See also: MallocChecker.cpp / M_ZERO. return; } } // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(1); const SVal V = state->getSVal(oflagsEx, C.getLocationContext()); if (!V.getAs<NonLoc>()) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. return; } NonLoc oflags = V.castAs<NonLoc>(); NonLoc ocreateFlag = C.getSValBuilder() .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); if (maskedFlagsUC.isUnknownOrUndef()) return; DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); // Check if maskedFlags is non-zero. ProgramStateRef trueState, falseState; std::tie(trueState, falseState) = state->assume(maskedFlags); // Only emit an error if the value of 'maskedFlags' is properly // constrained; if (!(trueState && !falseState)) return; if (CE->getNumArgs() < 3) { ReportOpenBug(C, trueState, "Call to 'open' requires a third argument when " "the 'O_CREAT' flag is set", oflagsEx->getSourceRange()); } }
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().Target.getTriple().getVendor() == llvm::Triple::Apple) Val_O_CREAT = 0x0200; else { // FIXME: We need a more general way of getting the O_CREAT value. // We could possibly grovel through the preprocessor state, but // that would require passing the Preprocessor object to the ExprEngine. return; } } // Look at the 'oflags' argument for the O_CREAT flag. const GRState *state = C.getState(); if (CE->getNumArgs() < 2) { // The frontend should issue a warning for this case, so this is a sanity // check. return; } // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(1); const SVal V = state->getSVal(oflagsEx); if (!isa<NonLoc>(V)) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. return; } NonLoc oflags = cast<NonLoc>(V); NonLoc ocreateFlag = cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType())); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); if (maskedFlagsUC.isUnknownOrUndef()) return; DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); // Check if maskedFlags is non-zero. const GRState *trueState, *falseState; llvm::tie(trueState, falseState) = state->assume(maskedFlags); // Only emit an error if the value of 'maskedFlags' is properly // constrained; if (!(trueState && !falseState)) return; if (CE->getNumArgs() < 3) { ExplodedNode *N = C.generateSink(trueState); if (!N) return; LazyInitialize(BT_open, "Improper use of 'open'"); RangedBugReport *report = new RangedBugReport(*BT_open, "Call to 'open' requires a third argument when " "the 'O_CREAT' flag is set", N); report->addRange(oflagsEx->getSourceRange()); C.EmitReport(report); } }
void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { unsigned idx = InvalidIdx; ProgramStateRef State = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; // If it is a call to an allocator function, it could be a double allocation. idx = getTrackedFunctionIndex(funName, true); if (idx != InvalidIdx) { const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) if (const AllocationState *AS = State->get<AllocatedData>(V)) { if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { // 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); if (!N) return; initBugType(); SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; os << "Allocated data should be released before another call to " << "the allocator: missing a call to '" << FunctionsToTrack[DIdx].Name << "'."; BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); } } return; } // Is it a call to one of deallocator functions? idx = getTrackedFunctionIndex(funName, false); if (idx == InvalidIdx) return; // Check the argument to the deallocator. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); // Undef is reported by another checker. if (ArgSVal.isUndef()) return; SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); // If the argument is coming from the heap, globals, or unknown, do not // report it. bool RegionArgIsBad = false; if (!ArgSM) { if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) return; RegionArgIsBad = true; } // Is the argument to the call being tracked? const AllocationState *AS = State->get<AllocatedData>(ArgSM); if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { return; } // If trying to free data which has not been allocated yet, report as a bug. // TODO: We might want a more precise diagnostic for double free // (that would involve tracking all the freed symbols in the checker state). if (!AS || RegionArgIsBad) { // It is possible that this is a false positive - the argument might // have entered as an enclosing function parameter. if (isEnclosingFunctionParam(ArgExpr)) return; ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) Report->markInteresting(AS->Region); C.emitReport(Report); return; } // Process functions which might deallocate. if (FunctionsToTrack[idx].Kind == PossibleAPI) { if (funName == "CFStringCreateWithBytesNoCopy") { const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); // NULL ~ default deallocator, so warn. if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), Expr::NPC_ValueDependentIsNotNull)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // One of the default allocators, so warn. if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { StringRef DeallocatorName = DE->getFoundDecl()->getName(); if (DeallocatorName == "kCFAllocatorDefault" || DeallocatorName == "kCFAllocatorSystemDefault" || DeallocatorName == "kCFAllocatorMalloc") { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If kCFAllocatorNull, which does not deallocate, we still have to // find the deallocator. if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") return; } // In all other cases, assume the user supplied a correct deallocator // that will free memory so stop tracking. State = State->remove<AllocatedData>(ArgSM); C.addTransition(State); return; } llvm_unreachable("We know of no other possible APIs."); } // The call is deallocating a value we previously allocated, so remove it // from the next state. State = State->remove<AllocatedData>(ArgSM); // Check if the proper deallocator is used. unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If the buffer can be null and the return status can be an error, // report a bad call to free. if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Only call free if a valid (non-NULL) buffer was returned.", N); Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); return; } C.addTransition(State); }
void IteratorChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // Record new iterator positions and iterator position changes const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!Func) return; if (Func->isOverloadedOperator()) { const auto Op = Func->getOverloadedOperator(); if (isSimpleComparisonOperator(Op)) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); } else { handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0), Call.getArgSVal(1), Op); } } } else { const auto *OrigExpr = Call.getOriginExpr(); if (!OrigExpr) return; if (!isIteratorType(Call.getResultType())) return; auto State = C.getState(); // Already bound to container? if (getIteratorPosition(State, Call.getReturnValue())) return; if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { if (isEndCall(Func)) { handleEnd(C, OrigExpr, Call.getReturnValue(), InstCall->getCXXThisVal()); return; } } // Copy-like and move constructors if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { State = setIteratorPosition(State, Call.getReturnValue(), *Pos); if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { State = removeIteratorPosition(State, Call.getArgSVal(0)); } C.addTransition(State); return; } } // Assumption: if return value is an iterator which is not yet bound to a // container, then look for the first iterator argument, and // bind the return value to the same container. This approach // works for STL algorithms. // FIXME: Add a more conservative mode for (unsigned i = 0; i < Call.getNumArgs(); ++i) { if (isIteratorType(Call.getArgExpr(i)->getType())) { if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { assignToContainer(C, OrigExpr, Call.getReturnValue(), Pos->getContainer()); return; } } } } }
ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE, CheckerContext &C) const { return C.getState()->addTaint(CE, C.getLocationContext()); }
bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE) { // Not enough arguments to match OSAtomicCompareAndSwap? if (CE->getNumArgs() != 3) return false; ASTContext &Ctx = C.getASTContext(); const Expr *oldValueExpr = CE->getArg(0); QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); const Expr *newValueExpr = CE->getArg(1); QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); // Do the types of 'oldValue' and 'newValue' match? if (oldValueType != newValueType) return false; const Expr *theValueExpr = CE->getArg(2); const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>(); // theValueType not a pointer? if (!theValueType) return false; QualType theValueTypePointee = Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); // The pointee must match newValueType and oldValueType. if (theValueTypePointee != newValueType) return false; static SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load"); static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store"); // Load 'theValue'. ExprEngine &Engine = C.getEngine(); const ProgramState *state = C.getState(); ExplodedNodeSet Tmp; SVal location = state->getSVal(theValueExpr); // Here we should use the value type of the region as the load type, because // we are simulating the semantics of the function, not the semantics of // passing argument. So the type of theValue expr is not we are loading. // But usually the type of the varregion is not the type we want either, // we still need to do a CastRetrievedVal in store manager. So actually this // LoadTy specifying can be omitted. But we put it here to emphasize the // semantics. QualType LoadTy; if (const TypedValueRegion *TR = dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { LoadTy = TR->getValueType(); } Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(), state, location, &OSAtomicLoadTag, LoadTy); if (Tmp.empty()) { // If no nodes were generated, other checkers must generated sinks. But // since the builder state was restored, we set it manually to prevent // auto transition. // FIXME: there should be a better approach. C.getNodeBuilder().BuildSinks = true; return true; } for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { ExplodedNode *N = *I; const ProgramState *stateLoad = N->getState(); // Use direct bindings from the environment since we are forcing a load // from a location that the Environment would typically not be used // to bind a value. SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, true); SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); // FIXME: Issue an error. if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { return false; } DefinedOrUnknownSVal theValueVal = cast<DefinedOrUnknownSVal>(theValueVal_untested); DefinedOrUnknownSVal oldValueVal = cast<DefinedOrUnknownSVal>(oldValueVal_untested); SValBuilder &svalBuilder = Engine.getSValBuilder(); // Perform the comparison. DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); const ProgramState *stateEqual = stateLoad->assume(Cmp, true); // Were they equal? if (stateEqual) { // Perform the store. ExplodedNodeSet TmpStore; SVal val = stateEqual->getSVal(newValueExpr); // Handle implicit value casts. if (const TypedValueRegion *R = dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); } Engine.evalStore(TmpStore, NULL, theValueExpr, N, stateEqual, location, val, &OSAtomicStoreTag); if (TmpStore.empty()) { // If no nodes were generated, other checkers must generated sinks. But // since the builder state was restored, we set it manually to prevent // auto transition. // FIXME: there should be a better approach. C.getNodeBuilder().BuildSinks = true; return true; } // Now bind the result of the comparison. for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), E2 = TmpStore.end(); I2 != E2; ++I2) { ExplodedNode *predNew = *I2; const ProgramState *stateNew = predNew->getState(); // Check for 'void' return type if we have a bogus function prototype. SVal Res = UnknownVal(); QualType T = CE->getType(); if (!T->isVoidType()) Res = Engine.getSValBuilder().makeTruthVal(true, T); C.generateNode(stateNew->BindExpr(CE, Res), predNew); } } // Were they not equal? if (const ProgramState *stateNotEqual = stateLoad->assume(Cmp, false)) { // Check for 'void' return type if we have a bogus function prototype. SVal Res = UnknownVal(); QualType T = CE->getType(); if (!T->isVoidType()) Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType()); C.generateNode(stateNotEqual->BindExpr(CE, Res), N); } } return true; }
void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; ASTContext &Ctx = C.getASTContext(); const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); if (!VLA) return; // FIXME: Handle multi-dimensional VLAs. const Expr* SE = VLA->getSizeExpr(); const GRState *state = C.getState(); SVal sizeV = state->getSVal(SE); if (sizeV.isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT_undef) BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " "garbage value as its size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // See if the size value is known. It can't be undefined because we would have // warned about that already. if (sizeV.isUnknown()) return; // Check if the size is zero. DefinedSVal sizeD = cast<DefinedSVal>(sizeV); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { ExplodedNode* N = C.generateSink(stateZero); if (!BT_zero) BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " "size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // From this point on, assume that the size is not zero. state = stateNotZero; // VLASizeChecker is responsible for defining the extent of the array being // declared. We do this by multiplying the array length by the element size, // then matching that with the array region's extent symbol. // Convert the array length to size_t. SValBuilder &svalBuilder = C.getSValBuilder(); QualType SizeTy = Ctx.getSizeType(); NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy, SE->getType())); // Get the element size. CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength, cast<NonLoc>(EleSizeVal), SizeTy); // Finally, assume that the array's extent matches the given size. const LocationContext *LC = C.getPredecessor()->getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); DefinedOrUnknownSVal sizeIsKnown = svalBuilder.evalEQ(state, Extent, ArraySize); state = state->assume(sizeIsKnown, true); // Assume should not fail at this point. assert(state); // Remember our assumptions! C.addTransition(state); }
void NonNullParamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { const Decl *FD = Call.getDecl(); if (!FD) return; const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); ProgramStateRef state = C.getState(); CallEvent::param_type_iterator TyI = Call.param_type_begin(), TyE = Call.param_type_end(); for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){ // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a paramter. bool haveRefTypeParam = false; if (TyI != TyE) { haveRefTypeParam = (*TyI)->isReferenceType(); TyI++; } bool haveAttrNonNull = Att && Att->isNonNull(idx); if (!haveAttrNonNull) { // Check if the parameter is also marked 'nonnull'. ArrayRef<ParmVarDecl*> parms = Call.parameters(); if (idx < parms.size()) haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); } if (!haveRefTypeParam && !haveAttrNonNull) continue; // If the value is unknown or undefined, we can't perform this check. const Expr *ArgE = Call.getArgExpr(idx); SVal V = Call.getArgSVal(idx); Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); if (!DV) continue; // Process the case when the argument is not a location. assert(!haveRefTypeParam || DV->getAs<Loc>()); if (haveAttrNonNull && !DV->getAs<Loc>()) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) continue; QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; if (Optional<nonloc::CompoundVal> CSV = DV->getAs<nonloc::CompoundVal>()) { nonloc::CompoundVal::iterator CSV_I = CSV->begin(); assert(CSV_I != CSV->end()); V = *CSV_I; DV = V.getAs<DefinedSVal>(); assert(++CSV_I == CSV->end()); // FIXME: Handle (some_union){ some_other_union_val }, which turns into // a LazyCompoundVal inside a CompoundVal. if (!V.getAs<Loc>()) continue; // Retrieve the corresponding expression. if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) if (const InitListExpr *IE = dyn_cast<InitListExpr>(CE->getInitializer())) ArgE = dyn_cast<Expr>(*(IE->begin())); } else { // FIXME: Handle LazyCompoundVals? continue; } } ConstraintManager &CM = C.getConstraintManager(); 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)) { BugReport *R = 0; 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)); // Emit the bug report. C.emitReport(R); } // Always return. Either we cached out or we just emitted an error. return; } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. assert(stateNotNull); state = stateNotNull; } // If we reach here all of the arguments passed the nonnull check. // If 'state' has been updated generated a new node. C.addTransition(state); }
void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { ProgramStateRef state = Ctx.getState(); // Iterate over all bindings to global variables and see if it contains // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; const StackFrameContext *CurSFC; public: SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getLocationContext()->getCurrentStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) { if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) return true; const MemRegion *vR = val.getAsRegion(); if (!vR) return true; // 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 = 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); if (cb.V.empty()) return; // Generate an error node. ExplodedNode *N = Ctx.addTransition(state); if (!N) return; if (!BT_stackleak) BT_stackleak.reset( new BuiltinBug("Stack address stored into global variable", "Stack address was saved into a global variable. " "This is dangerous because the address will become " "invalid after returning from the function")); for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { // Generate a report for this bug. SmallString<512> buf; llvm::raw_svector_ostream os(buf); SourceRange range = GenName(os, cb.V[i].second, Ctx.getSourceManager()); os << " is still referred to by the global variable '"; const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); os << *VR->getDecl() << "' upon returning to the caller. This will be a dangling reference"; BugReport *report = new BugReport(*BT_stackleak, os.str(), N); if (range.isValid()) report->addRange(range); Ctx.EmitReport(report); } }
void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, const CallExpr* CE) { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; // Get the function declaration of the callee. const GRState* state = C.getState(); SVal X = state->getSVal(CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return; if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); BT = new APIMisuse("null passed to CFRetain/CFRelease"); } // Check if we called CFRetain/CFRelease. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release)) return; // FIXME: The rest of this just checks that the argument is non-null. // It should probably be refactored and combined with AttrNonNullChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? const GRState *stateTrue, *stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateSink(stateTrue); if (!N) return; const char *description = (FuncII == Retain) ? "Null pointer argument in call to CFRetain" : "Null pointer argument in call to CFRelease"; EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); C.EmitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
void DynamicTypePropagation::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // We can obtain perfect type info for return values from some calls. if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { // Get the returned value if it's a region. const MemRegion *RetReg = Call.getReturnValue().getAsRegion(); if (!RetReg) return; ProgramStateRef State = C.getState(); switch (Msg->getMethodFamily()) { default: break; // We assume that the type of the object returned by alloc and new are the // pointer to the object of the class specified in the receiver of the // message. case OMF_alloc: case OMF_new: { // Get the type of object that will get created. const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); if (!ObjTy) return; QualType DynResTy = C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); break; } case OMF_init: { // Assume, the result of the init method has the same dynamic type as // the receiver and propagate the dynamic type info. const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); if (!RecReg) return; DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); break; } } return; } if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { // We may need to undo the effects of our pre-call check. switch (Ctor->getOriginExpr()->getConstructionKind()) { case CXXConstructExpr::CK_Complete: case CXXConstructExpr::CK_Delegating: // No additional work necessary. // Note: This will leave behind the actual type of the object for // complete constructors, but arguably that's a good thing, since it // means the dynamic type info will be correct even for objects // constructed with operator new. return; case CXXConstructExpr::CK_NonVirtualBase: case CXXConstructExpr::CK_VirtualBase: if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { // We just finished a base constructor. Now we can use the subclass's // type when resolving virtual calls. const Decl *D = C.getLocationContext()->getDecl(); recordFixedType(Target, cast<CXXConstructorDecl>(D), C); } return; } } }
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " "Objective-C pointer types")); ASTContext &Ctx = C.getASTContext(); arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); } if (!isVariadicMessage(msg)) return; // We are not interested in the selector arguments since they have // well-defined types, so the compiler will issue a warning for them. unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); // We're not interested in the last argument since it has to be nil or the // compiler would have issued a warning for it elsewhere. unsigned variadicArgsEnd = msg.getNumArgs() - 1; if (variadicArgsEnd <= variadicArgsBegin) return; // Verify that all arguments have Objective-C types. Optional<ExplodedNode*> errorNode; ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; // Block pointers are treaded as Objective-C pointers. if (ArgTy->isBlockPointerType()) continue; // 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(); if (!errorNode.getValue()) continue; SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); StringRef TypeName = GetReceiverInterfaceName(msg); if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() << "' should be an Objective-C pointer type, not '"; ArgTy.print(os, C.getLangOpts()); os << "'"; BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.emitReport(R); } }
void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // Find or create an entry in the hash for this BinaryOperator instance. // If we haven't done a lookup before, it will get default initialized to // 'Possible'. At this stage we do not store the ExplodedNode, as it has not // been created yet. BinaryOperatorData &Data = hash[B]; Assumption &A = Data.assumption; AnalysisDeclContext *AC = C.getCurrentAnalysisDeclContext(); // If we already have visited this node on a path that does not contain an // idempotent operation, return immediately. if (A == Impossible) return; // Retrieve both sides of the operator and determine if they can vary (which // may mean this is a false positive. const Expr *LHS = B->getLHS(); const Expr *RHS = B->getRHS(); // At this stage we can calculate whether each side contains a false positive // that applies to all operators. We only need to calculate this the first // time. bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false; if (A == Possible) { // An expression contains a false positive if it can't vary, or if it // contains a known false positive VarDecl. LHSContainsFalsePositive = !CanVary(LHS, AC) || containsNonLocalVarDecl(LHS); RHSContainsFalsePositive = !CanVary(RHS, AC) || containsNonLocalVarDecl(RHS); } ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); SVal LHSVal = state->getSVal(LHS, LCtx); SVal RHSVal = state->getSVal(RHS, LCtx); // If either value is unknown, we can't be 100% sure of all paths. if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) { A = Impossible; return; } BinaryOperator::Opcode Op = B->getOpcode(); // Dereference the LHS SVal if this is an assign operation switch (Op) { default: break; // Fall through intentional case BO_AddAssign: case BO_SubAssign: case BO_MulAssign: case BO_DivAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_ShlAssign: case BO_ShrAssign: case BO_Assign: // Assign statements have one extra level of indirection if (!isa<Loc>(LHSVal)) { A = Impossible; return; } LHSVal = state->getSVal(cast<Loc>(LHSVal), LHS->getType()); } // We now check for various cases which result in an idempotent operation. // x op x switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_Assign: // x Assign x can be used to silence unused variable warnings intentionally. // If this is a self assignment and the variable is referenced elsewhere, // and the assignment is not a truncation or extension, then it is a false // positive. if (isSelfAssign(LHS, RHS)) { if (!isUnused(LHS, AC) && !isTruncationExtensionAssignment(LHS, RHS)) { UpdateAssumption(A, Equal); return; } else { A = Impossible; return; } } case BO_SubAssign: case BO_DivAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_Sub: case BO_Div: case BO_And: case BO_Or: case BO_Xor: case BO_LOr: case BO_LAnd: case BO_EQ: case BO_NE: if (LHSVal != RHSVal || LHSContainsFalsePositive || RHSContainsFalsePositive) break; UpdateAssumption(A, Equal); return; } // x op 1 switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_MulAssign: case BO_DivAssign: case BO_Mul: case BO_Div: case BO_LOr: case BO_LAnd: if (!RHSVal.isConstant(1) || RHSContainsFalsePositive) break; UpdateAssumption(A, RHSis1); return; } // 1 op x switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_MulAssign: case BO_Mul: case BO_LOr: case BO_LAnd: if (!LHSVal.isConstant(1) || LHSContainsFalsePositive) break; UpdateAssumption(A, LHSis1); return; } // x op 0 switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_AddAssign: case BO_SubAssign: case BO_MulAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_Add: case BO_Sub: case BO_Mul: case BO_And: case BO_Or: case BO_Xor: case BO_Shl: case BO_Shr: case BO_LOr: case BO_LAnd: if (!RHSVal.isConstant(0) || RHSContainsFalsePositive) break; UpdateAssumption(A, RHSis0); return; } // 0 op x switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional //case BO_AddAssign: // Common false positive case BO_SubAssign: // Check only if unsigned case BO_MulAssign: case BO_DivAssign: case BO_AndAssign: //case BO_OrAssign: // Common false positive //case BO_XorAssign: // Common false positive case BO_ShlAssign: case BO_ShrAssign: case BO_Add: case BO_Sub: case BO_Mul: case BO_Div: case BO_And: case BO_Or: case BO_Xor: case BO_Shl: case BO_Shr: case BO_LOr: case BO_LAnd: if (!LHSVal.isConstant(0) || LHSContainsFalsePositive) break; UpdateAssumption(A, LHSis0); return; } // If we get to this point, there has been a valid use of this operation. A = Impossible; }
void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); Autorelease = &Ctx.Idents.get("CFAutorelease"); BT.reset(new APIMisuse( this, "null passed to CF memory management function")); } // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable || FuncII == Autorelease)) return; // FIXME: The rest of this just checks that the argument is non-null. // It should probably be refactored and combined with NonNullParamChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal zero = svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? ProgramStateRef stateTrue, stateFalse; std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateErrorNode(stateTrue); if (!N) return; const char *description; if (FuncII == Retain) description = "Null pointer argument in call to CFRetain"; else if (FuncII == Release) description = "Null pointer argument in call to CFRelease"; else if (FuncII == MakeCollectable) description = "Null pointer argument in call to CFMakeCollectable"; else if (FuncII == Autorelease) description = "Null pointer argument in call to CFAutorelease"; else llvm_unreachable("impossible case"); auto report = llvm::make_unique<BugReport>(*BT, description, N); report->addRange(Arg->getSourceRange()); bugreporter::trackNullOrUndefValue(N, Arg, *report); C.emitReport(std::move(report)); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; ASTContext &Ctx = C.getASTContext(); if (!II_fopen) II_fopen = &Ctx.Idents.get("fopen"); if (!II_tmpfile) II_tmpfile = &Ctx.Idents.get("tmpfile"); if (!II_fclose) II_fclose = &Ctx.Idents.get("fclose"); if (!II_fread) II_fread = &Ctx.Idents.get("fread"); if (!II_fwrite) II_fwrite = &Ctx.Idents.get("fwrite"); if (!II_fseek) II_fseek = &Ctx.Idents.get("fseek"); if (!II_ftell) II_ftell = &Ctx.Idents.get("ftell"); if (!II_rewind) II_rewind = &Ctx.Idents.get("rewind"); if (!II_fgetpos) II_fgetpos = &Ctx.Idents.get("fgetpos"); if (!II_fsetpos) II_fsetpos = &Ctx.Idents.get("fsetpos"); if (!II_clearerr) II_clearerr = &Ctx.Idents.get("clearerr"); if (!II_feof) II_feof = &Ctx.Idents.get("feof"); if (!II_ferror) II_ferror = &Ctx.Idents.get("ferror"); if (!II_fileno) II_fileno = &Ctx.Idents.get("fileno"); if (FD->getIdentifier() == II_fopen) { Fopen(C, CE); return true; } if (FD->getIdentifier() == II_tmpfile) { Tmpfile(C, CE); return true; } if (FD->getIdentifier() == II_fclose) { Fclose(C, CE); return true; } if (FD->getIdentifier() == II_fread) { Fread(C, CE); return true; } if (FD->getIdentifier() == II_fwrite) { Fwrite(C, CE); return true; } if (FD->getIdentifier() == II_fseek) { Fseek(C, CE); return true; } if (FD->getIdentifier() == II_ftell) { Ftell(C, CE); return true; } if (FD->getIdentifier() == II_rewind) { Rewind(C, CE); return true; } if (FD->getIdentifier() == II_fgetpos) { Fgetpos(C, CE); return true; } if (FD->getIdentifier() == II_fsetpos) { Fsetpos(C, CE); return true; } if (FD->getIdentifier() == II_clearerr) { Clearerr(C, CE); return true; } if (FD->getIdentifier() == II_feof) { Feof(C, CE); return true; } if (FD->getIdentifier() == II_ferror) { Ferror(C, CE); return true; } if (FD->getIdentifier() == II_fileno) { Fileno(C, CE); return true; } return false; }