@@ -28,7 +28,8 @@ using namespace ento;
2828namespace {
2929class MacOSKeychainAPIChecker : public Checker <check::PreStmt<CallExpr>,
3030 check::PostStmt<CallExpr>,
31- check::DeadSymbols> {
31+ check::DeadSymbols,
32+ eval::Assume> {
3233 mutable std::unique_ptr<BugType> BT;
3334
3435public:
@@ -57,6 +58,10 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
5758 void checkPreStmt (const CallExpr *S, CheckerContext &C) const ;
5859 void checkPostStmt (const CallExpr *S, CheckerContext &C) const ;
5960 void checkDeadSymbols (SymbolReaper &SR, CheckerContext &C) const ;
61+ ProgramStateRef evalAssume (ProgramStateRef state, SVal Cond,
62+ bool Assumption) const ;
63+ void printState (raw_ostream &Out, ProgramStateRef State,
64+ const char *NL, const char *Sep) const ;
6065
6166private:
6267 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
@@ -106,19 +111,6 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
106111 std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport (
107112 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const ;
108113
109- // / Check if RetSym evaluates to an error value in the current state.
110- bool definitelyReturnedError (SymbolRef RetSym,
111- ProgramStateRef State,
112- SValBuilder &Builder,
113- bool noError = false ) const ;
114-
115- // / Check if RetSym evaluates to a NoErr value in the current state.
116- bool definitelyDidnotReturnError (SymbolRef RetSym,
117- ProgramStateRef State,
118- SValBuilder &Builder) const {
119- return definitelyReturnedError (RetSym, State, Builder, true );
120- }
121-
122114 // / Mark an AllocationPair interesting for diagnostic reporting.
123115 void markInteresting (BugReport *R, const AllocationPair &AP) const {
124116 R->markInteresting (AP.first );
@@ -221,24 +213,6 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr,
221213 return nullptr ;
222214}
223215
224- // When checking for error code, we need to consider the following cases:
225- // 1) noErr / [0]
226- // 2) someErr / [1, inf]
227- // 3) unknown
228- // If noError, returns true iff (1).
229- // If !noError, returns true iff (2).
230- bool MacOSKeychainAPIChecker::definitelyReturnedError (SymbolRef RetSym,
231- ProgramStateRef State,
232- SValBuilder &Builder,
233- bool noError) const {
234- DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal (NoErr,
235- Builder.getSymbolManager ().getType (RetSym));
236- DefinedOrUnknownSVal NoErr = Builder.evalEQ (State, NoErrVal,
237- nonloc::SymbolVal (RetSym));
238- ProgramStateRef ErrState = State->assume (NoErr, noError);
239- return ErrState == State;
240- }
241-
242216// Report deallocator mismatch. Remove the region from tracking - reporting a
243217// missing free error after this one is redundant.
244218void MacOSKeychainAPIChecker::
@@ -289,27 +263,25 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
289263 const Expr *ArgExpr = CE->getArg (paramIdx);
290264 if (SymbolRef V = getAsPointeeSymbol (ArgExpr, C))
291265 if (const AllocationState *AS = State->get <AllocatedData>(V)) {
292- if (!definitelyReturnedError (AS->Region , State, C.getSValBuilder ())) {
293- // Remove the value from the state. The new symbol will be added for
294- // tracking when the second allocator is processed in checkPostStmt().
295- State = State->remove <AllocatedData>(V);
296- ExplodedNode *N = C.generateNonFatalErrorNode (State);
297- if (!N)
298- return ;
299- initBugType ();
300- SmallString<128 > sbuf;
301- llvm::raw_svector_ostream os (sbuf);
302- unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx ].DeallocatorIdx ;
303- os << " Allocated data should be released before another call to "
304- << " the allocator: missing a call to '"
305- << FunctionsToTrack[DIdx].Name
306- << " '." ;
307- auto Report = llvm::make_unique<BugReport>(*BT, os.str (), N);
308- Report->addVisitor (llvm::make_unique<SecKeychainBugVisitor>(V));
309- Report->addRange (ArgExpr->getSourceRange ());
310- Report->markInteresting (AS->Region );
311- C.emitReport (std::move (Report));
312- }
266+ // Remove the value from the state. The new symbol will be added for
267+ // tracking when the second allocator is processed in checkPostStmt().
268+ State = State->remove <AllocatedData>(V);
269+ ExplodedNode *N = C.generateNonFatalErrorNode (State);
270+ if (!N)
271+ return ;
272+ initBugType ();
273+ SmallString<128 > sbuf;
274+ llvm::raw_svector_ostream os (sbuf);
275+ unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx ].DeallocatorIdx ;
276+ os << " Allocated data should be released before another call to "
277+ << " the allocator: missing a call to '"
278+ << FunctionsToTrack[DIdx].Name
279+ << " '." ;
280+ auto Report = llvm::make_unique<BugReport>(*BT, os.str (), N);
281+ Report->addVisitor (llvm::make_unique<SecKeychainBugVisitor>(V));
282+ Report->addRange (ArgExpr->getSourceRange ());
283+ Report->markInteresting (AS->Region );
284+ C.emitReport (std::move (Report));
313285 }
314286 return ;
315287 }
@@ -344,13 +316,12 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
344316
345317 // Is the argument to the call being tracked?
346318 const AllocationState *AS = State->get <AllocatedData>(ArgSM);
347- if (!AS && FunctionsToTrack[idx]. Kind != ValidAPI) {
319+ if (!AS)
348320 return ;
349- }
350- // If trying to free data which has not been allocated yet, report as a bug.
351- // TODO: We might want a more precise diagnostic for double free
321+
322+ // TODO: We might want to report double free here.
352323 // (that would involve tracking all the freed symbols in the checker state).
353- if (!AS || RegionArgIsBad) {
324+ if (RegionArgIsBad) {
354325 // It is possible that this is a false positive - the argument might
355326 // have entered as an enclosing function parameter.
356327 if (isEnclosingFunctionParam (ArgExpr))
@@ -418,23 +389,6 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
418389 return ;
419390 }
420391
421- // If the buffer can be null and the return status can be an error,
422- // report a bad call to free.
423- if (State->assume (ArgSVal.castAs <DefinedSVal>(), false ) &&
424- !definitelyDidnotReturnError (AS->Region , State, C.getSValBuilder ())) {
425- ExplodedNode *N = C.generateNonFatalErrorNode (State);
426- if (!N)
427- return ;
428- initBugType ();
429- auto Report = llvm::make_unique<BugReport>(
430- *BT, " Only call free if a valid (non-NULL) buffer was returned." , N);
431- Report->addVisitor (llvm::make_unique<SecKeychainBugVisitor>(ArgSM));
432- Report->addRange (ArgExpr->getSourceRange ());
433- Report->markInteresting (AS->Region );
434- C.emitReport (std::move (Report));
435- return ;
436- }
437-
438392 C.addTransition (State);
439393}
440394
@@ -540,27 +494,63 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
540494 return Report;
541495}
542496
497+ // / If the return symbol is assumed to be error, remove the allocated info
498+ // / from consideration.
499+ ProgramStateRef MacOSKeychainAPIChecker::evalAssume (ProgramStateRef State,
500+ SVal Cond,
501+ bool Assumption) const {
502+ AllocatedDataTy AMap = State->get <AllocatedData>();
503+ if (AMap.isEmpty ())
504+ return State;
505+
506+ auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr ());
507+ if (!CondBSE)
508+ return State;
509+ BinaryOperator::Opcode OpCode = CondBSE->getOpcode ();
510+ if (OpCode != BO_EQ && OpCode != BO_NE)
511+ return State;
512+
513+ // Match for a restricted set of patterns for cmparison of error codes.
514+ // Note, the comparisons of type '0 == st' are transformed into SymIntExpr.
515+ SymbolRef ReturnSymbol = nullptr ;
516+ if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
517+ const llvm::APInt &RHS = SIE->getRHS ();
518+ bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
519+ (OpCode == BO_NE && RHS == NoErr);
520+ if (!Assumption)
521+ ErrorIsReturned = !ErrorIsReturned;
522+ if (ErrorIsReturned)
523+ ReturnSymbol = SIE->getLHS ();
524+ }
525+
526+ if (ReturnSymbol)
527+ for (auto I = AMap.begin (), E = AMap.end (); I != E; ++I) {
528+ if (ReturnSymbol == I->second .Region )
529+ State = State->remove <AllocatedData>(I->first );
530+ }
531+
532+ return State;
533+ }
534+
543535void MacOSKeychainAPIChecker::checkDeadSymbols (SymbolReaper &SR,
544536 CheckerContext &C) const {
545537 ProgramStateRef State = C.getState ();
546- AllocatedDataTy ASet = State->get <AllocatedData>();
547- if (ASet .isEmpty ())
538+ AllocatedDataTy AMap = State->get <AllocatedData>();
539+ if (AMap .isEmpty ())
548540 return ;
549541
550542 bool Changed = false ;
551543 AllocationPairVec Errors;
552- for (AllocatedDataTy::iterator I = ASet .begin (), E = ASet .end (); I != E; ++I) {
553- if (SR.isLive (I->first ))
544+ for (auto I = AMap .begin (), E = AMap .end (); I != E; ++I) {
545+ if (! SR.isDead (I->first ))
554546 continue ;
555547
556548 Changed = true ;
557549 State = State->remove <AllocatedData>(I->first );
558- // If the allocated symbol is null or if the allocation call might have
559- // returned an error, do not report.
550+ // If the allocated symbol is null do not report.
560551 ConstraintManager &CMgr = State->getConstraintManager ();
561552 ConditionTruthVal AllocFailed = CMgr.isNull (State, I.getKey ());
562- if (AllocFailed.isConstrainedTrue () ||
563- definitelyReturnedError (I->second .Region , State, C.getSValBuilder ()))
553+ if (AllocFailed.isConstrainedTrue ())
564554 continue ;
565555 Errors.push_back (std::make_pair (I->first , &I->second ));
566556 }
@@ -612,6 +602,22 @@ MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
612602 " Data is allocated here." );
613603}
614604
605+ void MacOSKeychainAPIChecker::printState (raw_ostream &Out,
606+ ProgramStateRef State,
607+ const char *NL,
608+ const char *Sep) const {
609+
610+ AllocatedDataTy AMap = State->get <AllocatedData>();
611+
612+ if (!AMap.isEmpty ()) {
613+ Out << Sep << " KeychainAPIChecker :" << NL;
614+ for (auto I = AMap.begin (), E = AMap.end (); I != E; ++I) {
615+ I.getKey ()->dumpToStream (Out);
616+ }
617+ }
618+ }
619+
620+
615621void ento::registerMacOSKeychainAPIChecker (CheckerManager &mgr) {
616622 mgr.registerChecker <MacOSKeychainAPIChecker>();
617623}
0 commit comments