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<BugReport> generateAllocatedDataNotReleasedReport(
117 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C)
const;
120 void markInteresting(BugReport *R,
const AllocationPair &AP)
const {
121 R->markInteresting(AP.first);
122 R->markInteresting(AP.second->Region);
134 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
136 void Profile(llvm::FoldingSetNodeID &ID)
const override {
142 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
143 BugReporterContext &BRC,
144 BugReport &BR)
override;
154 MacOSKeychainAPIChecker::AllocationState)
156 static bool isEnclosingFunctionParam(
const Expr *E) {
158 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
160 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
166 const MacOSKeychainAPIChecker::ADFunctionInfo
167 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
168 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
169 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
170 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
171 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
172 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
173 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
174 {
"free", 0, InvalidIdx, ErrorAPI},
175 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
178 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
180 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
181 ADFunctionInfo FI = FunctionsToTrack[I];
185 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
187 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
199 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
200 isa<TypedRegion>(Arg);
208 SVal ArgV = C.getSVal(Expr);
211 StoreManager&
SM = C.getStoreManager();
212 SymbolRef sym = SM.getBinding(State->getStore(), *
X).getAsLocSymbol();
221 void MacOSKeychainAPIChecker::
222 generateDeallocatorMismatchReport(
const AllocationPair &AP,
224 CheckerContext &C)
const {
226 State = State->remove<AllocatedData>(AP.first);
227 ExplodedNode *N = C.generateNonFatalErrorNode(State);
233 llvm::raw_svector_ostream os(sbuf);
234 unsigned int PDeallocIdx =
235 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
237 os <<
"Deallocator doesn't match the allocator: '" 238 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
239 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
240 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
242 markInteresting(Report.get(), AP);
243 C.emitReport(std::move(Report));
246 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
247 CheckerContext &C)
const {
248 unsigned idx = InvalidIdx;
255 StringRef funName = C.getCalleeName(FD);
260 idx = getTrackedFunctionIndex(funName,
true);
261 if (idx != InvalidIdx) {
262 unsigned paramIdx = FunctionsToTrack[idx].Param;
268 if (
const AllocationState *AS = State->get<AllocatedData>(
V)) {
271 State = State->remove<AllocatedData>(
V);
272 ExplodedNode *N = C.generateNonFatalErrorNode(State);
277 llvm::raw_svector_ostream os(sbuf);
278 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
279 os <<
"Allocated data should be released before another call to " 280 <<
"the allocator: missing a call to '" 281 << FunctionsToTrack[DIdx].Name
283 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
284 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(
V));
286 Report->markInteresting(AS->Region);
287 C.emitReport(std::move(Report));
293 idx = getTrackedFunctionIndex(funName,
false);
294 if (idx == InvalidIdx)
297 unsigned paramIdx = FunctionsToTrack[idx].Param;
303 SVal ArgSVal = C.getSVal(ArgExpr);
306 if (ArgSVal.isUndef())
309 SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
313 bool RegionArgIsBad =
false;
317 RegionArgIsBad =
true;
321 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
327 if (RegionArgIsBad) {
330 if (isEnclosingFunctionParam(ArgExpr))
333 ExplodedNode *N = C.generateNonFatalErrorNode(State);
337 auto Report = llvm::make_unique<BugReport>(
338 *BT,
"Trying to free data which has not been allocated.", N);
341 Report->markInteresting(AS->Region);
342 C.emitReport(std::move(Report));
347 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
349 if (funName ==
"CFStringCreateWithBytesNoCopy") {
354 const AllocationPair AP = std::make_pair(ArgSM, AS);
355 generateDeallocatorMismatchReport(AP, ArgExpr, C);
359 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
360 StringRef DeallocatorName = DE->getFoundDecl()->getName();
361 if (DeallocatorName ==
"kCFAllocatorDefault" ||
362 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
363 DeallocatorName ==
"kCFAllocatorMalloc") {
364 const AllocationPair AP = std::make_pair(ArgSM, AS);
365 generateDeallocatorMismatchReport(AP, ArgExpr, C);
370 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
375 State = State->remove<AllocatedData>(ArgSM);
376 C.addTransition(State);
380 llvm_unreachable(
"We know of no other possible APIs.");
385 State = State->remove<AllocatedData>(ArgSM);
388 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
389 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
390 const AllocationPair AP = std::make_pair(ArgSM, AS);
391 generateDeallocatorMismatchReport(AP, ArgExpr, C);
395 C.addTransition(State);
398 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
399 CheckerContext &C)
const {
405 StringRef funName = C.getCalleeName(FD);
408 unsigned idx = getTrackedFunctionIndex(funName,
true);
409 if (idx == InvalidIdx)
412 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
415 if (isEnclosingFunctionParam(ArgExpr) &&
416 C.getLocationContext()->getParent() ==
nullptr)
432 SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
433 C.getSymbolManager().addSymbolDependency(
V, RetStatusSymbol);
436 State = State->set<AllocatedData>(
V, AllocationState(ArgExpr, idx,
439 C.addTransition(State);
445 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
447 CheckerContext &C)
const {
451 const ExplodedNode *AllocNode = N;
454 if (!N->getState()->get<AllocatedData>(Sym))
459 if (NContext == LeakContext ||
462 N = N->pred_empty() ? nullptr : *(N->pred_begin());
468 std::unique_ptr<BugReport>
469 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
470 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C)
const {
471 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
474 llvm::raw_svector_ostream os(sbuf);
475 os <<
"Allocated data is not released: missing a call to '" 476 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
481 PathDiagnosticLocation LocUsedForUniqueing;
482 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
487 C.getSourceManager(),
488 AllocNode->getLocationContext());
491 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
492 AllocNode->getLocationContext()->getDecl());
494 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
495 markInteresting(Report.get(), AP);
503 bool Assumption)
const {
504 AllocatedDataTy AMap = State->get<AllocatedData>();
508 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
512 if (OpCode != BO_EQ && OpCode != BO_NE)
518 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
519 const llvm::APInt &RHS = SIE->getRHS();
520 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
521 (OpCode == BO_NE && RHS == NoErr);
523 ErrorIsReturned = !ErrorIsReturned;
525 ReturnSymbol = SIE->getLHS();
529 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
530 if (ReturnSymbol == I->second.Region)
531 State = State->remove<AllocatedData>(I->first);
537 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
538 CheckerContext &C)
const {
540 AllocatedDataTy AMap = State->get<AllocatedData>();
544 bool Changed =
false;
545 AllocationPairVec Errors;
546 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
547 if (!SR.isDead(I->first))
551 State = State->remove<AllocatedData>(I->first);
553 ConstraintManager &CMgr = State->getConstraintManager();
554 ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
555 if (AllocFailed.isConstrainedTrue())
557 Errors.push_back(std::make_pair(I->first, &I->second));
561 C.addTransition(State);
565 static CheckerProgramPointTag Tag(
this,
"DeadSymbolsLeak");
566 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag);
571 for (
const auto &
P : Errors)
572 C.emitReport(generateAllocatedDataNotReleasedReport(
P, N, C));
575 C.addTransition(State, N);
583 if (!Call || Call->getDecl())
586 for (
auto I : State->get<AllocatedData>()) {
588 if (Escaped.count(Sym))
589 State = State->remove<AllocatedData>(Sym);
607 if (
const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
608 SymbolRef ParentSym = SD->getParentSymbol();
609 if (Escaped.count(ParentSym))
610 State = State->remove<AllocatedData>(Sym);
616 std::shared_ptr<PathDiagnosticPiece>
617 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
618 const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
619 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
622 const AllocationState *ASPrev =
623 N->getFirstPred()->getState()->get<AllocatedData>(Sym);
630 cast<CallExpr>(N->getLocation().castAs<
StmtPoint>().getStmt());
632 assert(funDecl &&
"We do not support indirect function calls as of now.");
633 StringRef funName = funDecl->getName();
636 unsigned Idx = getTrackedFunctionIndex(funName,
true);
637 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
638 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
639 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
640 N->getLocationContext());
641 return std::make_shared<PathDiagnosticEventPiece>(Pos,
642 "Data is allocated here.");
645 void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
648 const char *Sep)
const {
650 AllocatedDataTy AMap = State->get<AllocatedData>();
652 if (!AMap.isEmpty()) {
653 Out << Sep <<
"KeychainAPIChecker :" << NL;
654 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
655 I.getKey()->dumpToStream(Out);
661 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
662 mgr.registerChecker<MacOSKeychainAPIChecker>();
665 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
const SymExpr * SymbolRef
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.
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
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 const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
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.