22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/Support/raw_ostream.h"
25 using namespace clang;
29 class MacOSKeychainAPIChecker :
public Checker<check::PreStmt<CallExpr>,
30 check::PostStmt<CallExpr>,
34 mutable std::unique_ptr<BugType> BT;
39 struct AllocationState {
41 unsigned int AllocatorIdx;
44 AllocationState(
const Expr *E,
unsigned int Idx,
SymbolRef R) :
49 return (AllocatorIdx ==
X.AllocatorIdx &&
53 void Profile(llvm::FoldingSetNodeID &
ID)
const {
54 ID.AddInteger(AllocatorIdx);
55 ID.AddPointer(Region);
59 void checkPreStmt(
const CallExpr *S, CheckerContext &C)
const;
60 void checkPostStmt(
const CallExpr *S, CheckerContext &C)
const;
61 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C)
const;
64 const CallEvent *Call,
67 bool Assumption)
const;
69 const char *NL,
const char *Sep)
const override;
72 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
86 struct ADFunctionInfo {
89 unsigned int DeallocatorIdx;
92 static const unsigned InvalidIdx = 100000;
93 static const unsigned FunctionsToTrackSize = 8;
94 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
96 static const unsigned NoErr = 0;
100 static unsigned getTrackedFunctionIndex(StringRef Name,
bool IsAllocator);
102 inline void initBugType()
const {
104 BT.reset(
new BugType(
this,
"Improper use of SecKeychain API",
105 "API Misuse (Apple)"));
108 void generateDeallocatorMismatchReport(
const AllocationPair &AP,
110 CheckerContext &C)
const;
113 const ExplodedNode *getAllocationNode(
const ExplodedNode *N,
SymbolRef Sym,
114 CheckerContext &C)
const;
116 std::unique_ptr<PathSensitiveBugReport>
117 generateAllocatedDataNotReleasedReport(
const AllocationPair &AP,
119 CheckerContext &C)
const;
122 void markInteresting(PathSensitiveBugReport *R,
123 const AllocationPair &AP)
const {
124 R->markInteresting(AP.first);
125 R->markInteresting(AP.second->Region);
131 class SecKeychainBugVisitor :
public BugReporterVisitor {
137 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
139 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
146 BugReporterContext &BRC,
147 PathSensitiveBugReport &BR)
override;
157 MacOSKeychainAPIChecker::AllocationState)
159 static bool isEnclosingFunctionParam(
const Expr *E) {
161 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
163 if (isa<ImplicitParamDecl, ParmVarDecl>(VD))
169 const MacOSKeychainAPIChecker::ADFunctionInfo
170 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
171 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
172 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
173 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
174 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
175 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
176 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
177 {
"free", 0, InvalidIdx, ErrorAPI},
178 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
181 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
183 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
184 ADFunctionInfo FI = FunctionsToTrack[I];
188 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
190 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
202 return isa<AllocaRegion, BlockDataRegion, TypedRegion>(Arg);
210 SVal ArgV = C.getSVal(
Expr);
213 StoreManager&
SM = C.getStoreManager();
223 void MacOSKeychainAPIChecker::
224 generateDeallocatorMismatchReport(
const AllocationPair &AP,
226 CheckerContext &C)
const {
228 State =
State->remove<AllocatedData>(AP.first);
229 ExplodedNode *N =
C.generateNonFatalErrorNode(
State);
235 llvm::raw_svector_ostream os(sbuf);
236 unsigned int PDeallocIdx =
237 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
239 os <<
"Deallocator doesn't match the allocator: '"
240 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
241 auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
242 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
244 markInteresting(Report.get(), AP);
245 C.emitReport(std::move(Report));
248 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
249 CheckerContext &C)
const {
250 unsigned idx = InvalidIdx;
254 if (!FD || FD->
getKind() != Decl::Function)
257 StringRef funName =
C.getCalleeName(FD);
262 idx = getTrackedFunctionIndex(funName,
true);
263 if (idx != InvalidIdx) {
264 unsigned paramIdx = FunctionsToTrack[idx].Param;
270 if (
const AllocationState *AS =
State->get<AllocatedData>(
V)) {
274 ExplodedNode *N =
C.generateNonFatalErrorNode(
State);
279 llvm::raw_svector_ostream os(sbuf);
280 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
281 os <<
"Allocated data should be released before another call to "
282 <<
"the allocator: missing a call to '"
283 << FunctionsToTrack[DIdx].Name
286 std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
287 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(
V));
289 Report->markInteresting(AS->Region);
290 C.emitReport(std::move(Report));
296 idx = getTrackedFunctionIndex(funName,
false);
297 if (idx == InvalidIdx)
300 unsigned paramIdx = FunctionsToTrack[idx].Param;
306 SVal ArgSVal =
C.getSVal(ArgExpr);
309 if (ArgSVal.isUndef())
312 SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
316 bool RegionArgIsBad =
false;
320 RegionArgIsBad =
true;
324 const AllocationState *AS =
State->get<AllocatedData>(ArgSM);
330 if (RegionArgIsBad) {
333 if (isEnclosingFunctionParam(ArgExpr))
336 ExplodedNode *N =
C.generateNonFatalErrorNode(
State);
340 auto Report = std::make_unique<PathSensitiveBugReport>(
341 *BT,
"Trying to free data which has not been allocated.", N);
344 Report->markInteresting(AS->Region);
345 C.emitReport(std::move(Report));
350 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
352 if (funName ==
"CFStringCreateWithBytesNoCopy") {
357 const AllocationPair AP = std::make_pair(ArgSM, AS);
358 generateDeallocatorMismatchReport(AP, ArgExpr, C);
362 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
363 StringRef DeallocatorName = DE->getFoundDecl()->getName();
364 if (DeallocatorName ==
"kCFAllocatorDefault" ||
365 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
366 DeallocatorName ==
"kCFAllocatorMalloc") {
367 const AllocationPair AP = std::make_pair(ArgSM, AS);
368 generateDeallocatorMismatchReport(AP, ArgExpr, C);
373 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
383 llvm_unreachable(
"We know of no other possible APIs.");
391 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
392 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
393 const AllocationPair AP = std::make_pair(ArgSM, AS);
394 generateDeallocatorMismatchReport(AP, ArgExpr, C);
401 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
402 CheckerContext &C)
const {
405 if (!FD || FD->
getKind() != Decl::Function)
408 StringRef funName =
C.getCalleeName(FD);
411 unsigned idx = getTrackedFunctionIndex(funName,
true);
412 if (idx == InvalidIdx)
415 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
418 if (isEnclosingFunctionParam(ArgExpr) &&
419 C.getLocationContext()->getParent() ==
nullptr)
435 SymbolRef RetStatusSymbol =
C.getSVal(CE).getAsSymbol();
436 C.getSymbolManager().addSymbolDependency(
V, RetStatusSymbol);
439 State =
State->set<AllocatedData>(
V, AllocationState(ArgExpr, idx,
448 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
450 CheckerContext &C)
const {
454 const ExplodedNode *AllocNode = N;
457 if (!N->getState()->get<AllocatedData>(Sym))
462 if (NContext == LeakContext ||
465 N = N->pred_empty() ? nullptr : *(N->pred_begin());
471 std::unique_ptr<PathSensitiveBugReport>
472 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
473 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C)
const {
474 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
477 llvm::raw_svector_ostream os(sbuf);
478 os <<
"Allocated data is not released: missing a call to '"
479 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
484 PathDiagnosticLocation LocUsedForUniqueing;
485 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
486 const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics();
490 C.getSourceManager(),
491 AllocNode->getLocationContext());
493 auto Report = std::make_unique<PathSensitiveBugReport>(
494 *BT, os.str(), N, LocUsedForUniqueing,
495 AllocNode->getLocationContext()->getDecl());
497 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
498 markInteresting(Report.get(), AP);
506 bool Assumption)
const {
507 AllocatedDataTy AMap =
State->get<AllocatedData>();
511 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
515 if (OpCode != BO_EQ && OpCode != BO_NE)
521 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
523 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
524 (OpCode == BO_NE && RHS == NoErr);
526 ErrorIsReturned = !ErrorIsReturned;
528 ReturnSymbol = SIE->getLHS();
532 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
533 if (ReturnSymbol == I->second.Region)
534 State =
State->remove<AllocatedData>(I->first);
540 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
541 CheckerContext &C)
const {
543 AllocatedDataTy AMap =
State->get<AllocatedData>();
548 AllocationPairVec Errors;
549 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
550 if (!SR.isDead(I->first))
554 State =
State->remove<AllocatedData>(I->first);
556 ConstraintManager &CMgr =
State->getConstraintManager();
557 ConditionTruthVal AllocFailed = CMgr.isNull(
State, I.getKey());
558 if (AllocFailed.isConstrainedTrue())
560 Errors.push_back(std::make_pair(I->first, &I->second));
568 static CheckerProgramPointTag Tag(
this,
"DeadSymbolsLeak");
569 ExplodedNode *N =
C.generateNonFatalErrorNode(
C.getState(), &Tag);
574 for (
const auto &
P : Errors)
575 C.emitReport(generateAllocatedDataNotReleasedReport(
P, N, C));
578 C.addTransition(
State, N);
586 if (!Call ||
Call->getDecl())
589 for (
auto I :
State->get<AllocatedData>()) {
591 if (Escaped.count(Sym))
610 if (
const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
611 SymbolRef ParentSym = SD->getParentSymbol();
612 if (Escaped.count(ParentSym))
620 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
621 const ExplodedNode *N, BugReporterContext &BRC,
622 PathSensitiveBugReport &BR) {
623 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
626 const AllocationState *ASPrev =
627 N->getFirstPred()->getState()->get<AllocatedData>(Sym);
636 assert(funDecl &&
"We do not support indirect function calls as of now.");
637 StringRef funName = funDecl->
getName();
640 unsigned Idx = getTrackedFunctionIndex(funName,
true);
641 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
642 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
643 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
644 N->getLocationContext());
645 return std::make_shared<PathDiagnosticEventPiece>(Pos,
646 "Data is allocated here.");
649 void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
652 const char *Sep)
const {
654 AllocatedDataTy AMap =
State->get<AllocatedData>();
656 if (!AMap.isEmpty()) {
657 Out << Sep <<
"KeychainAPIChecker :" << NL;
658 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
659 I.getKey()->dumpToStream(Out);
665 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
666 mgr.registerChecker<MacOSKeychainAPIChecker>();
669 bool ento::shouldRegisterMacOSKeychainAPIChecker(
const CheckerManager &mgr) {