23 #include "llvm/ADT/SmallString.h" 24 #include "llvm/Support/raw_ostream.h" 26 using namespace clang;
30 class MacOSKeychainAPIChecker :
public Checker<check::PreStmt<CallExpr>,
31 check::PostStmt<CallExpr>,
35 mutable std::unique_ptr<BugType> BT;
40 struct AllocationState {
42 unsigned int AllocatorIdx;
45 AllocationState(
const Expr *E,
unsigned int Idx,
SymbolRef R) :
50 return (AllocatorIdx == X.AllocatorIdx &&
54 void Profile(llvm::FoldingSetNodeID &
ID)
const {
55 ID.AddInteger(AllocatorIdx);
56 ID.AddPointer(Region);
60 void checkPreStmt(
const CallExpr *S, CheckerContext &C)
const;
61 void checkPostStmt(
const CallExpr *S, CheckerContext &C)
const;
62 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C)
const;
68 bool Assumption)
const;
70 const char *NL,
const char *Sep)
const;
73 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
87 struct ADFunctionInfo {
90 unsigned int DeallocatorIdx;
93 static const unsigned InvalidIdx = 100000;
94 static const unsigned FunctionsToTrackSize = 8;
95 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
97 static const unsigned NoErr = 0;
101 static unsigned getTrackedFunctionIndex(StringRef Name,
bool IsAllocator);
103 inline void initBugType()
const {
105 BT.reset(
new BugType(
this,
"Improper use of SecKeychain API",
106 "API Misuse (Apple)"));
109 void generateDeallocatorMismatchReport(
const AllocationPair &AP,
111 CheckerContext &C)
const;
114 const ExplodedNode *getAllocationNode(
const ExplodedNode *N,
SymbolRef Sym,
115 CheckerContext &C)
const;
117 std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport(
118 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C)
const;
121 void markInteresting(BugReport *R,
const AllocationPair &AP)
const {
122 R->markInteresting(AP.first);
123 R->markInteresting(AP.second->Region);
135 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
137 void Profile(llvm::FoldingSetNodeID &ID)
const override {
143 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
144 BugReporterContext &BRC,
145 BugReport &BR)
override;
155 MacOSKeychainAPIChecker::AllocationState)
157 static bool isEnclosingFunctionParam(
const Expr *E) {
159 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
161 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
167 const MacOSKeychainAPIChecker::ADFunctionInfo
168 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
169 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
170 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
171 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
172 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
173 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
174 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
175 {
"free", 0, InvalidIdx, ErrorAPI},
176 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
179 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
181 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
182 ADFunctionInfo FI = FunctionsToTrack[I];
186 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
188 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
200 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
201 isa<TypedRegion>(Arg);
209 SVal ArgV = C.getSVal(Expr);
212 StoreManager&
SM = C.getStoreManager();
213 SymbolRef sym = SM.getBinding(State->getStore(), *
X).getAsLocSymbol();
222 void MacOSKeychainAPIChecker::
223 generateDeallocatorMismatchReport(
const AllocationPair &AP,
225 CheckerContext &C)
const {
227 State = State->remove<AllocatedData>(AP.first);
228 ExplodedNode *N = C.generateNonFatalErrorNode(State);
234 llvm::raw_svector_ostream os(sbuf);
235 unsigned int PDeallocIdx =
236 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
238 os <<
"Deallocator doesn't match the allocator: '" 239 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
240 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
241 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
243 markInteresting(Report.get(), AP);
244 C.emitReport(std::move(Report));
247 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
248 CheckerContext &C)
const {
249 unsigned idx = InvalidIdx;
256 StringRef funName = C.getCalleeName(FD);
261 idx = getTrackedFunctionIndex(funName,
true);
262 if (idx != InvalidIdx) {
263 unsigned paramIdx = FunctionsToTrack[idx].Param;
269 if (
const AllocationState *AS = State->get<AllocatedData>(V)) {
272 State = State->remove<AllocatedData>(V);
273 ExplodedNode *N = C.generateNonFatalErrorNode(State);
278 llvm::raw_svector_ostream os(sbuf);
279 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
280 os <<
"Allocated data should be released before another call to " 281 <<
"the allocator: missing a call to '" 282 << FunctionsToTrack[DIdx].Name
284 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
285 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
287 Report->markInteresting(AS->Region);
288 C.emitReport(std::move(Report));
294 idx = getTrackedFunctionIndex(funName,
false);
295 if (idx == InvalidIdx)
298 unsigned paramIdx = FunctionsToTrack[idx].Param;
304 SVal ArgSVal = C.getSVal(ArgExpr);
307 if (ArgSVal.isUndef())
310 SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
314 bool RegionArgIsBad =
false;
318 RegionArgIsBad =
true;
322 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
328 if (RegionArgIsBad) {
331 if (isEnclosingFunctionParam(ArgExpr))
334 ExplodedNode *N = C.generateNonFatalErrorNode(State);
338 auto Report = llvm::make_unique<BugReport>(
339 *BT,
"Trying to free data which has not been allocated.", N);
342 Report->markInteresting(AS->Region);
343 C.emitReport(std::move(Report));
348 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
350 if (funName ==
"CFStringCreateWithBytesNoCopy") {
355 const AllocationPair AP = std::make_pair(ArgSM, AS);
356 generateDeallocatorMismatchReport(AP, ArgExpr, C);
360 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
361 StringRef DeallocatorName = DE->getFoundDecl()->getName();
362 if (DeallocatorName ==
"kCFAllocatorDefault" ||
363 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
364 DeallocatorName ==
"kCFAllocatorMalloc") {
365 const AllocationPair AP = std::make_pair(ArgSM, AS);
366 generateDeallocatorMismatchReport(AP, ArgExpr, C);
371 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
376 State = State->remove<AllocatedData>(ArgSM);
377 C.addTransition(State);
381 llvm_unreachable(
"We know of no other possible APIs.");
386 State = State->remove<AllocatedData>(ArgSM);
389 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
390 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
391 const AllocationPair AP = std::make_pair(ArgSM, AS);
392 generateDeallocatorMismatchReport(AP, ArgExpr, C);
396 C.addTransition(State);
399 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
400 CheckerContext &C)
const {
406 StringRef funName = C.getCalleeName(FD);
409 unsigned idx = getTrackedFunctionIndex(funName,
true);
410 if (idx == InvalidIdx)
413 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
416 if (isEnclosingFunctionParam(ArgExpr) &&
417 C.getLocationContext()->getParent() ==
nullptr)
433 SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
434 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
437 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
440 C.addTransition(State);
446 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
448 CheckerContext &C)
const {
452 const ExplodedNode *AllocNode = N;
455 if (!N->getState()->get<AllocatedData>(Sym))
460 if (NContext == LeakContext ||
463 N = N->pred_empty() ? nullptr : *(N->pred_begin());
469 std::unique_ptr<BugReport>
470 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
471 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C)
const {
472 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
475 llvm::raw_svector_ostream os(sbuf);
476 os <<
"Allocated data is not released: missing a call to '" 477 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
482 PathDiagnosticLocation LocUsedForUniqueing;
483 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
488 C.getSourceManager(),
489 AllocNode->getLocationContext());
492 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
493 AllocNode->getLocationContext()->getDecl());
495 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
496 markInteresting(Report.get(), AP);
504 bool Assumption)
const {
505 AllocatedDataTy AMap = State->get<AllocatedData>();
509 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
513 if (OpCode != BO_EQ && OpCode != BO_NE)
519 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
520 const llvm::APInt &RHS = SIE->getRHS();
521 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
522 (OpCode == BO_NE && RHS == NoErr);
524 ErrorIsReturned = !ErrorIsReturned;
526 ReturnSymbol = SIE->getLHS();
530 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
531 if (ReturnSymbol == I->second.Region)
532 State = State->remove<AllocatedData>(I->first);
538 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
539 CheckerContext &C)
const {
541 AllocatedDataTy AMap = State->get<AllocatedData>();
545 bool Changed =
false;
546 AllocationPairVec Errors;
547 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
548 if (!SR.isDead(I->first))
552 State = State->remove<AllocatedData>(I->first);
554 ConstraintManager &CMgr = State->getConstraintManager();
555 ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
556 if (AllocFailed.isConstrainedTrue())
558 Errors.push_back(std::make_pair(I->first, &I->second));
562 C.addTransition(State);
566 static CheckerProgramPointTag Tag(
this,
"DeadSymbolsLeak");
567 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag);
572 for (
const auto &
P : Errors)
573 C.emitReport(generateAllocatedDataNotReleasedReport(
P, N, C));
576 C.addTransition(State, N);
584 if (!Call || Call->getDecl())
587 for (
auto I : State->get<AllocatedData>()) {
589 if (Escaped.count(Sym))
590 State = State->remove<AllocatedData>(Sym);
608 if (
const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
609 SymbolRef ParentSym = SD->getParentSymbol();
610 if (Escaped.count(ParentSym))
611 State = State->remove<AllocatedData>(Sym);
617 std::shared_ptr<PathDiagnosticPiece>
618 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
619 const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
620 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
623 const AllocationState *ASPrev =
624 N->getFirstPred()->getState()->get<AllocatedData>(Sym);
631 cast<CallExpr>(N->getLocation().castAs<
StmtPoint>().getStmt());
633 assert(funDecl &&
"We do not support indirect function calls as of now.");
634 StringRef funName = funDecl->getName();
637 unsigned Idx = getTrackedFunctionIndex(funName,
true);
638 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
639 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
640 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
641 N->getLocationContext());
642 return std::make_shared<PathDiagnosticEventPiece>(Pos,
643 "Data is allocated here.");
646 void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
649 const char *Sep)
const {
651 AllocatedDataTy AMap = State->get<AllocatedData>();
653 if (!AMap.isEmpty()) {
654 Out << Sep <<
"KeychainAPIChecker :" << NL;
655 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
656 I.getKey()->dumpToStream(Out);
662 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
663 mgr.registerChecker<MacOSKeychainAPIChecker>();
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.
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
IgnoreParenCasts - Ignore parentheses and casts.
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.