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); } } } }
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 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); } }
// 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 CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, ExplodedNode *N) const { if (!BT_msg_ret) BT_msg_ret.reset( new BuiltinBug("Receiver in message expression is " "'nil' and returns a garbage value")); llvm::SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '" << msg.getSelector().getAsString() << "' is nil and returns a value of type '" << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); if (const Expr *receiver = msg.getInstanceReceiver()) { report->addRange(receiver->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver)); } C.EmitReport(report); }
// 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 { const ProgramState *State = C.getState(); State = State->remove<AllocatedData>(AP.first); ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); llvm::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."; BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(AP.first)); Report->addRange(ArgExpr->getSourceRange()); C.EmitReport(Report); }
BugReport *MacOSKeychainAPIChecker:: generateAllocatedDataNotReleasedReport(const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { const ADSubprogramInfo &FI = SubprogramsToTrack[AP.second->AllocatorIdx]; initBugType(); SmallString<70> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Allocated data is not released: missing a call to '" << SubprogramsToTrack[FI.DeallocatorIdx].Name << "'."; // Most bug reports are cached at the location where they occurred. // With leaks, we want to unique them by the location where they were // allocated, and only report a single path. PathDiagnosticLocation LocUsedForUniqueing; if (const Stmt *AllocStmt = getAllocationSite(N, AP.first, C)) LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, C.getSourceManager(), N->getLocationContext()); BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing); Report->addVisitor(new SecKeychainBugVisitor(AP.first)); markInteresting(Report, AP); return 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 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); }
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *ErrorNode, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !ErrorNode) return false; if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) S = OVE->getSourceExpr(); const ExplodedNode *N = ErrorNode; const Expr *Inner = 0; if (const Expr *Ex = dyn_cast<Expr>(S)) { Ex = Ex->IgnoreParenCasts(); if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)) Inner = Ex; } if (IsArg) { assert(N->getLocation().getAs<CallEnter>() && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. // Alternately, if we hit a known lvalue for the statement, we know we've // gone too far (though we can likely track the lvalue better anyway). do { const ProgramPoint &pp = N->getLocation(); if (Optional<PostStmt> ps = pp.getAs<PostStmt>()) { if (ps->getStmt() == S || ps->getStmt() == Inner) break; } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) { if (CEE->getCalleeContext()->getCallSite() == S || CEE->getCalleeContext()->getCallSite() == Inner) break; } N = N->getFirstPred(); } while (N); if (!N) return false; } ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { const MemRegion *R = 0; // First check if this is a DeclRefExpr for a C++ reference type. // For those, we want the location of the reference. if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Inner)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { if (VD->getType()->isReferenceType()) { ProgramStateManager &StateMgr = state->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); R = MRMgr.getVarRegion(VD, N->getLocationContext()); } } } // For all other cases, find the location by scouring the ExplodedGraph. if (!R) { // Find the ExplodedNode where the lvalue (the value of 'Ex') // was computed. We need this for getting the location value. const ExplodedNode *LVNode = N; while (LVNode) { if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) { if (P->getStmt() == Inner) break; } LVNode = LVNode->getFirstPred(); } assert(LVNode && "Unable to find the lvalue node."); ProgramStateRef LVState = LVNode->getState(); R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion(); } if (R) { // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); // If the value matches the default for the variable region, that // might mean that it's been cleared out of the state. Fall back to // the full argument expression (with casts and such intact). if (IsArg) { bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); if (!UseArgValue) { const SymbolRegionValue *SRV = dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); if (SRV) UseArgValue = (SRV->getRegion() == R); } if (UseArgValue) V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); } report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); if (isa<SymbolicRegion>(R)) { TrackConstraintBRVisitor *VI = new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); report.addVisitor(VI); } // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); report.addVisitor(ConstraintTracker); // Add visitor, which will suppress inline defensive checks. if (ErrorNode->getState()->isNull(V).isConstrainedTrue()) { BugReporterVisitor *IDCSuppressor = new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(), ErrorNode); report.addVisitor(IDCSuppressor); } } if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) report.addVisitor(new FindLastStoreBRVisitor(*KV, R)); return true; } } // If the expression is not an "lvalue expression", we can still // track the constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // If the value came from an inlined function call, we should at least make // sure that function isn't pruned in our output. if (const Expr *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } } return true; }
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !N) return false; if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) S = OVE->getSourceExpr(); if (IsArg) { assert(isa<CallEnter>(N->getLocation()) && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. do { const ProgramPoint &pp = N->getLocation(); if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { if (ps->getStmt() == S) break; } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { if (CEE->getCalleeContext()->getCallSite() == S) break; } N = N->getFirstPred(); } while (N); if (!N) return false; } ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (const Expr *Ex = dyn_cast<Expr>(S)) { // Strip off parens and casts. Note that this will never have issues with // C++ user-defined implicit conversions, because those have a constructor // or function call inside. Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { // FIXME: Right now we only track VarDecls because it's non-trivial to // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { ProgramStateManager &StateMgr = state->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); const VarRegion *R = MRMgr.getVarRegion(VD, N->getLocationContext()); // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); // If the value matches the default for the variable region, that // might mean that it's been cleared out of the state. Fall back to // the full argument expression (with casts and such intact). if (IsArg) { bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); if (!UseArgValue) { const SymbolRegionValue *SRV = dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); if (SRV) UseArgValue = (SRV->getRegion() == R); } if (UseArgValue) V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); } report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(cast<DefinedSVal>(V), false); report.addVisitor(ConstraintTracker); } report.addVisitor(new FindLastStoreBRVisitor(V, R)); return true; } } } // If the expression does NOT refer to a variable, we can still track // constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } } else { // Otherwise, if the value came from an inlined function call, // we should at least make sure that function isn't pruned in our output. if (const Expr *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report); } return true; }
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"); BT.reset(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, C.getLocationContext()); 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? ProgramStateRef 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"; BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg, report)); C.EmitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
void AttrNonNullChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { const Decl *FD = Call.getDecl(); if (!FD) return; const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); if (!Att) return; ProgramStateRef state = C.getState(); // Iterate through the arguments of CE and check them for null. for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) { if (!Att->isNonNull(idx)) continue; SVal V = Call.getArgSVal(idx); DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); // If the value is unknown or undefined, we can't perform this check. if (!DV) continue; if (!isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. const Expr *ArgE = Call.getArgExpr(idx); if (!ArgE) continue; QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) { nonloc::CompoundVal::iterator CSV_I = CSV->begin(); assert(CSV_I != CSV->end()); V = *CSV_I; DV = dyn_cast<DefinedSVal>(&V); assert(++CSV_I == CSV->end()); if (!DV) continue; } else { // FIXME: Handle LazyCompoundVals? continue; } } ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef stateNotNull, stateNull; llvm::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)) { // Lazily allocate the BugType object if it hasn't already been // created. Ownership is transferred to the BugReporter object once // the BugReport is passed to 'EmitWarning'. if (!BT) BT.reset(new BugType("Argument with 'nonnull' attribute passed null", "API")); BugReport *R = new BugReport(*BT, "Null pointer passed as an argument to a " "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. R->addRange(Call.getArgSourceRange(idx)); if (const Expr *ArgE = Call.getArgExpr(idx)) R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode, ArgE, R)); // 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 bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &report) { if (!S || !N) return; ProgramStateManager &StateMgr = N->getState()->getStateManager(); // Walk through nodes until we get one that matches the statement exactly. while (N) { const ProgramPoint &pp = N->getLocation(); if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { if (ps->getStmt() == S) break; } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { if (CEE->getCalleeContext()->getCallSite() == S) break; } N = N->getFirstPred(); } if (!N) return; ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (const Expr *Ex = dyn_cast<Expr>(S)) { // Strip off parens and casts. Note that this will never have issues with // C++ user-defined implicit conversions, because those have a constructor // or function call inside. Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { // FIXME: Right now we only track VarDecls because it's non-trivial to // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); report.markInteresting(R); report.markInteresting(V); // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(cast<loc::MemRegionVal>(V), false); report.addVisitor(ConstraintTracker); } report.addVisitor(new FindLastStoreBRVisitor(V, R)); return; } } } // If the expression does NOT refer to a variable, we can still track // constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { const MemRegion *Base = L->getRegion()->getBaseRegion(); if (isa<SymbolicRegion>(Base)) { report.markInteresting(Base); report.addVisitor(new TrackConstraintBRVisitor(loc::MemRegionVal(Base), false)); } } else { // Otherwise, if the value came from an inlined function call, // we should at least make sure that function isn't pruned in our output. ReturnVisitor::addVisitorIfNecessary(N, S, report); } }