@@ -28,7 +28,8 @@ using namespace ento;
28
28
namespace {
29
29
class MacOSKeychainAPIChecker : public Checker <check::PreStmt<CallExpr>,
30
30
check::PostStmt<CallExpr>,
31
- check::DeadSymbols> {
31
+ check::DeadSymbols,
32
+ eval::Assume> {
32
33
mutable std::unique_ptr<BugType> BT;
33
34
34
35
public:
@@ -57,6 +58,10 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
57
58
void checkPreStmt (const CallExpr *S, CheckerContext &C) const ;
58
59
void checkPostStmt (const CallExpr *S, CheckerContext &C) const ;
59
60
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 ;
60
65
61
66
private:
62
67
typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
@@ -106,19 +111,6 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
106
111
std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport (
107
112
const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const ;
108
113
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
-
122
114
// / Mark an AllocationPair interesting for diagnostic reporting.
123
115
void markInteresting (BugReport *R, const AllocationPair &AP) const {
124
116
R->markInteresting (AP.first );
@@ -221,24 +213,6 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr,
221
213
return nullptr ;
222
214
}
223
215
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
-
242
216
// Report deallocator mismatch. Remove the region from tracking - reporting a
243
217
// missing free error after this one is redundant.
244
218
void MacOSKeychainAPIChecker::
@@ -289,27 +263,25 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
289
263
const Expr *ArgExpr = CE->getArg (paramIdx);
290
264
if (SymbolRef V = getAsPointeeSymbol (ArgExpr, C))
291
265
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));
313
285
}
314
286
return ;
315
287
}
@@ -344,13 +316,12 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
344
316
345
317
// Is the argument to the call being tracked?
346
318
const AllocationState *AS = State->get <AllocatedData>(ArgSM);
347
- if (!AS && FunctionsToTrack[idx]. Kind != ValidAPI) {
319
+ if (!AS)
348
320
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.
352
323
// (that would involve tracking all the freed symbols in the checker state).
353
- if (!AS || RegionArgIsBad) {
324
+ if (RegionArgIsBad) {
354
325
// It is possible that this is a false positive - the argument might
355
326
// have entered as an enclosing function parameter.
356
327
if (isEnclosingFunctionParam (ArgExpr))
@@ -418,23 +389,6 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
418
389
return ;
419
390
}
420
391
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
-
438
392
C.addTransition (State);
439
393
}
440
394
@@ -540,27 +494,63 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
540
494
return Report;
541
495
}
542
496
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
+
543
535
void MacOSKeychainAPIChecker::checkDeadSymbols (SymbolReaper &SR,
544
536
CheckerContext &C) const {
545
537
ProgramStateRef State = C.getState ();
546
- AllocatedDataTy ASet = State->get <AllocatedData>();
547
- if (ASet .isEmpty ())
538
+ AllocatedDataTy AMap = State->get <AllocatedData>();
539
+ if (AMap .isEmpty ())
548
540
return ;
549
541
550
542
bool Changed = false ;
551
543
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 ))
554
546
continue ;
555
547
556
548
Changed = true ;
557
549
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.
560
551
ConstraintManager &CMgr = State->getConstraintManager ();
561
552
ConditionTruthVal AllocFailed = CMgr.isNull (State, I.getKey ());
562
- if (AllocFailed.isConstrainedTrue () ||
563
- definitelyReturnedError (I->second .Region , State, C.getSValBuilder ()))
553
+ if (AllocFailed.isConstrainedTrue ())
564
554
continue ;
565
555
Errors.push_back (std::make_pair (I->first , &I->second ));
566
556
}
@@ -612,6 +602,22 @@ MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
612
602
" Data is allocated here." );
613
603
}
614
604
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
+
615
621
void ento::registerMacOSKeychainAPIChecker (CheckerManager &mgr) {
616
622
mgr.registerChecker <MacOSKeychainAPIChecker>();
617
623
}
0 commit comments