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;
67 bool Assumption)
const;
69 const char *NL,
const char *Sep)
const;
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);
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>(VD) || isa<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>(Arg) || isa<BlockDataRegion>(Arg) ||
203 isa<TypedRegion>(Arg);
211 SVal ArgV = C.getSVal(Expr);
214 StoreManager&
SM = C.getStoreManager();
215 SymbolRef sym = SM.getBinding(State->getStore(), *
X).getAsLocSymbol();
224 void MacOSKeychainAPIChecker::
225 generateDeallocatorMismatchReport(
const AllocationPair &AP,
227 CheckerContext &C)
const {
229 State = State->remove<AllocatedData>(AP.first);
230 ExplodedNode *N = C.generateNonFatalErrorNode(State);
236 llvm::raw_svector_ostream os(sbuf);
237 unsigned int PDeallocIdx =
238 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
240 os <<
"Deallocator doesn't match the allocator: '" 241 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
242 auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
243 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
245 markInteresting(Report.get(), AP);
246 C.emitReport(std::move(Report));
249 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
250 CheckerContext &C)
const {
251 unsigned idx = InvalidIdx;
258 StringRef funName = C.getCalleeName(FD);
263 idx = getTrackedFunctionIndex(funName,
true);
264 if (idx != InvalidIdx) {
265 unsigned paramIdx = FunctionsToTrack[idx].Param;
271 if (
const AllocationState *AS = State->get<AllocatedData>(
V)) {
274 State = State->remove<AllocatedData>(
V);
275 ExplodedNode *N = C.generateNonFatalErrorNode(State);
280 llvm::raw_svector_ostream os(sbuf);
281 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
282 os <<
"Allocated data should be released before another call to " 283 <<
"the allocator: missing a call to '" 284 << FunctionsToTrack[DIdx].Name
287 std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
288 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(
V));
290 Report->markInteresting(AS->Region);
291 C.emitReport(std::move(Report));
297 idx = getTrackedFunctionIndex(funName,
false);
298 if (idx == InvalidIdx)
301 unsigned paramIdx = FunctionsToTrack[idx].Param;
307 SVal ArgSVal = C.getSVal(ArgExpr);
310 if (ArgSVal.isUndef())
313 SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
317 bool RegionArgIsBad =
false;
321 RegionArgIsBad =
true;
325 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
331 if (RegionArgIsBad) {
334 if (isEnclosingFunctionParam(ArgExpr))
337 ExplodedNode *N = C.generateNonFatalErrorNode(State);
341 auto Report = std::make_unique<PathSensitiveBugReport>(
342 *BT,
"Trying to free data which has not been allocated.", N);
345 Report->markInteresting(AS->Region);
346 C.emitReport(std::move(Report));
351 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
353 if (funName ==
"CFStringCreateWithBytesNoCopy") {
358 const AllocationPair AP = std::make_pair(ArgSM, AS);
359 generateDeallocatorMismatchReport(AP, ArgExpr, C);
363 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
364 StringRef DeallocatorName = DE->getFoundDecl()->getName();
365 if (DeallocatorName ==
"kCFAllocatorDefault" ||
366 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
367 DeallocatorName ==
"kCFAllocatorMalloc") {
368 const AllocationPair AP = std::make_pair(ArgSM, AS);
369 generateDeallocatorMismatchReport(AP, ArgExpr, C);
374 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
379 State = State->remove<AllocatedData>(ArgSM);
380 C.addTransition(State);
384 llvm_unreachable(
"We know of no other possible APIs.");
389 State = State->remove<AllocatedData>(ArgSM);
392 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
393 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
394 const AllocationPair AP = std::make_pair(ArgSM, AS);
395 generateDeallocatorMismatchReport(AP, ArgExpr, C);
399 C.addTransition(State);
402 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
403 CheckerContext &C)
const {
409 StringRef funName = C.getCalleeName(FD);
412 unsigned idx = getTrackedFunctionIndex(funName,
true);
413 if (idx == InvalidIdx)
416 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
419 if (isEnclosingFunctionParam(ArgExpr) &&
420 C.getLocationContext()->getParent() ==
nullptr)
436 SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
437 C.getSymbolManager().addSymbolDependency(
V, RetStatusSymbol);
440 State = State->set<AllocatedData>(
V, AllocationState(ArgExpr, idx,
443 C.addTransition(State);
449 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
451 CheckerContext &C)
const {
455 const ExplodedNode *AllocNode = N;
458 if (!N->getState()->get<AllocatedData>(Sym))
463 if (NContext == LeakContext ||
466 N = N->pred_empty() ? nullptr : *(N->pred_begin());
472 std::unique_ptr<PathSensitiveBugReport>
473 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
474 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C)
const {
475 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
478 llvm::raw_svector_ostream os(sbuf);
479 os <<
"Allocated data is not released: missing a call to '" 480 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
485 PathDiagnosticLocation LocUsedForUniqueing;
486 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
487 const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics();
491 C.getSourceManager(),
492 AllocNode->getLocationContext());
494 auto Report = std::make_unique<PathSensitiveBugReport>(
495 *BT, os.str(), N, LocUsedForUniqueing,
496 AllocNode->getLocationContext()->getDecl());
498 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
499 markInteresting(Report.get(), AP);
507 bool Assumption)
const {
508 AllocatedDataTy AMap = State->get<AllocatedData>();
512 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
516 if (OpCode != BO_EQ && OpCode != BO_NE)
522 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
524 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
525 (OpCode == BO_NE && RHS == NoErr);
527 ErrorIsReturned = !ErrorIsReturned;
529 ReturnSymbol = SIE->getLHS();
533 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
534 if (ReturnSymbol == I->second.Region)
535 State = State->remove<AllocatedData>(I->first);
541 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
542 CheckerContext &C)
const {
544 AllocatedDataTy AMap = State->get<AllocatedData>();
548 bool Changed =
false;
549 AllocationPairVec Errors;
550 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
551 if (!SR.isDead(I->first))
555 State = State->remove<AllocatedData>(I->first);
557 ConstraintManager &CMgr = State->getConstraintManager();
558 ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
559 if (AllocFailed.isConstrainedTrue())
561 Errors.push_back(std::make_pair(I->first, &I->second));
565 C.addTransition(State);
569 static CheckerProgramPointTag Tag(
this,
"DeadSymbolsLeak");
570 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag);
575 for (
const auto &
P : Errors)
576 C.emitReport(generateAllocatedDataNotReleasedReport(
P, N, C));
579 C.addTransition(State, N);
587 if (!Call || Call->getDecl())
590 for (
auto I : State->get<AllocatedData>()) {
592 if (Escaped.count(Sym))
593 State = State->remove<AllocatedData>(Sym);
611 if (
const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
612 SymbolRef ParentSym = SD->getParentSymbol();
613 if (Escaped.count(ParentSym))
614 State = State->remove<AllocatedData>(Sym);
621 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
622 const ExplodedNode *N, BugReporterContext &BRC,
623 PathSensitiveBugReport &BR) {
624 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
627 const AllocationState *ASPrev =
628 N->getFirstPred()->getState()->get<AllocatedData>(Sym);
635 cast<CallExpr>(N->getLocation().castAs<
StmtPoint>().getStmt());
637 assert(funDecl &&
"We do not support indirect function calls as of now.");
638 StringRef funName = funDecl->getName();
641 unsigned Idx = getTrackedFunctionIndex(funName,
true);
642 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
643 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
644 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
645 N->getLocationContext());
646 return std::make_shared<PathDiagnosticEventPiece>(Pos,
647 "Data is allocated here.");
650 void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
653 const char *Sep)
const {
655 AllocatedDataTy AMap = State->get<AllocatedData>();
657 if (!AMap.isEmpty()) {
658 Out << Sep <<
"KeychainAPIChecker :" << NL;
659 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
660 I.getKey()->dumpToStream(Out);
666 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
667 mgr.registerChecker<MacOSKeychainAPIChecker>();
670 bool ento::shouldRegisterMacOSKeychainAPIChecker(
const LangOptions &LO) {
Represents a function declaration or definition.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
bool operator==(CanQual< T > x, CanQual< U > y)
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Stmt - This represents one statement.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
constexpr XRayInstrMask Function
bool isParentOf(const LocationContext *LC) const
static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)
Given the address expression, retrieve the value it's pointing to.
const SymExpr * SymbolRef
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
i32 captured_struct **param SharedsTy A type which contains references the shared variables *param Shareds Context with the list of shared variables from the p *TaskFunction *param Data Additional data for task generation like final * state
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
This represents one expression.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static bool isBadDeallocationArgument(const MemRegion *Arg)
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant...
Specifies that a value-dependent expression should be considered to never be a null pointer constant...
REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, SymbolRef, MacOSKeychainAPIChecker::AllocationState) static bool isEnclosingFunctionParam(const Expr *E)
ProgramState traits to store the currently allocated (and not yet freed) symbols. ...
Dataflow Directional Tag Classes.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
A reference to a declared variable, function, enum, etc.