22#include "llvm/ADT/STLExtras.h"
23#include "llvm/Support/raw_ostream.h"
30class MacOSKeychainAPIChecker :
public Checker<check::PreStmt<CallExpr>,
31 check::PostStmt<CallExpr>,
35 const BugType BT{
this,
"Improper use of SecKeychain API",
41 struct AllocationState {
43 unsigned int AllocatorIdx;
46 AllocationState(
const Expr *E,
unsigned int Idx,
SymbolRef R) :
51 return (AllocatorIdx ==
X.AllocatorIdx &&
55 void Profile(llvm::FoldingSetNodeID &ID)
const {
56 ID.AddInteger(AllocatorIdx);
57 ID.AddPointer(Region);
61 void checkPreStmt(
const CallExpr *S, CheckerContext &
C)
const;
62 void checkPostStmt(
const CallExpr *S, CheckerContext &
C)
const;
63 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &
C)
const;
66 const CallEvent *
Call,
69 bool Assumption)
const;
71 const char *NL,
const char *Sep)
const override;
74 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
75 typedef SmallVector<AllocationPair, 2> AllocationPairVec;
88 struct ADFunctionInfo {
91 unsigned int DeallocatorIdx;
94 static const unsigned InvalidIdx = 100000;
95 static const unsigned FunctionsToTrackSize = 8;
96 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
98 static const unsigned NoErr = 0;
102 static unsigned getTrackedFunctionIndex(StringRef Name,
bool IsAllocator);
104 void generateDeallocatorMismatchReport(
const AllocationPair &AP,
106 CheckerContext &
C)
const;
109 const ExplodedNode *getAllocationNode(
const ExplodedNode *N,
SymbolRef Sym,
110 CheckerContext &
C)
const;
112 std::unique_ptr<PathSensitiveBugReport>
113 generateAllocatedDataNotReleasedReport(
const AllocationPair &AP,
115 CheckerContext &
C)
const;
118 void markInteresting(PathSensitiveBugReport *R,
119 const AllocationPair &AP)
const {
127 class SecKeychainBugVisitor :
public BugReporterVisitor {
133 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
135 void Profile(llvm::FoldingSetNodeID &ID)
const override {
142 BugReporterContext &BRC,
143 PathSensitiveBugReport &BR)
override;
153 MacOSKeychainAPIChecker::AllocationState)
155static bool isEnclosingFunctionParam(
const Expr *E) {
157 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
165const MacOSKeychainAPIChecker::ADFunctionInfo
166 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
167 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
168 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
169 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
170 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
171 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
172 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
173 {
"free", 0, InvalidIdx, ErrorAPI},
174 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
177unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
179 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
180 ADFunctionInfo FI = FunctionsToTrack[I];
184 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
186 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
210 SymbolRef sym =
SM.getBinding(State->getStore(), *
X).getAsLocSymbol();
219void MacOSKeychainAPIChecker::
220 generateDeallocatorMismatchReport(
const AllocationPair &AP,
222 CheckerContext &
C)
const {
224 State = State->remove<AllocatedData>(AP.first);
225 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
229 SmallString<80> sbuf;
230 llvm::raw_svector_ostream os(sbuf);
231 unsigned int PDeallocIdx =
232 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
234 os <<
"Deallocator doesn't match the allocator: '"
235 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
236 auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
237 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
239 markInteresting(
Report.get(), AP);
240 C.emitReport(std::move(
Report));
243void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
244 CheckerContext &
C)
const {
245 unsigned idx = InvalidIdx;
248 const FunctionDecl *FD =
C.getCalleeDecl(CE);
249 if (!FD || FD->
getKind() != Decl::Function)
252 StringRef funName =
C.getCalleeName(FD);
257 idx = getTrackedFunctionIndex(funName,
true);
258 if (idx != InvalidIdx) {
259 unsigned paramIdx = FunctionsToTrack[idx].Param;
263 const Expr *ArgExpr = CE->
getArg(paramIdx);
265 if (
const AllocationState *AS = State->get<AllocatedData>(
V)) {
268 State = State->remove<AllocatedData>(
V);
269 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
272 SmallString<128> sbuf;
273 llvm::raw_svector_ostream os(sbuf);
274 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
275 os <<
"Allocated data should be released before another call to "
276 <<
"the allocator: missing a call to '"
277 << FunctionsToTrack[DIdx].Name
279 auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
280 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(
V));
282 Report->markInteresting(AS->Region);
283 C.emitReport(std::move(
Report));
289 idx = getTrackedFunctionIndex(funName,
false);
290 if (idx == InvalidIdx)
293 unsigned paramIdx = FunctionsToTrack[idx].Param;
298 const Expr *ArgExpr = CE->
getArg(paramIdx);
299 SVal ArgSVal =
C.getSVal(ArgExpr);
309 bool RegionArgIsBad =
false;
313 RegionArgIsBad =
true;
317 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
323 if (RegionArgIsBad) {
326 if (isEnclosingFunctionParam(ArgExpr))
329 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
332 auto Report = std::make_unique<PathSensitiveBugReport>(
333 BT,
"Trying to free data which has not been allocated.", N);
336 Report->markInteresting(AS->Region);
337 C.emitReport(std::move(
Report));
342 if (FunctionsToTrack[idx].Kind == PossibleAPI) {
344 if (funName ==
"CFStringCreateWithBytesNoCopy") {
349 const AllocationPair AP = std::make_pair(ArgSM, AS);
350 generateDeallocatorMismatchReport(AP, ArgExpr,
C);
354 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
355 StringRef DeallocatorName = DE->getFoundDecl()->getName();
356 if (DeallocatorName ==
"kCFAllocatorDefault" ||
357 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
358 DeallocatorName ==
"kCFAllocatorMalloc") {
359 const AllocationPair AP = std::make_pair(ArgSM, AS);
360 generateDeallocatorMismatchReport(AP, ArgExpr,
C);
365 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
370 State = State->remove<AllocatedData>(ArgSM);
371 C.addTransition(State);
375 llvm_unreachable(
"We know of no other possible APIs.");
380 State = State->remove<AllocatedData>(ArgSM);
383 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
384 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
385 const AllocationPair AP = std::make_pair(ArgSM, AS);
386 generateDeallocatorMismatchReport(AP, ArgExpr,
C);
390 C.addTransition(State);
393void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
394 CheckerContext &
C)
const {
396 const FunctionDecl *FD =
C.getCalleeDecl(CE);
397 if (!FD || FD->
getKind() != Decl::Function)
400 StringRef funName =
C.getCalleeName(FD);
403 unsigned idx = getTrackedFunctionIndex(funName,
true);
404 if (idx == InvalidIdx)
407 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
410 if (isEnclosingFunctionParam(ArgExpr) &&
411 C.getLocationContext()->getParent() ==
nullptr)
427 SymbolRef RetStatusSymbol =
C.getSVal(CE).getAsSymbol();
428 C.getSymbolManager().addSymbolDependency(
V, RetStatusSymbol);
431 State = State->set<AllocatedData>(
V, AllocationState(ArgExpr, idx,
434 C.addTransition(State);
440MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
442 CheckerContext &
C)
const {
446 const ExplodedNode *AllocNode = N;
449 if (!N->
getState()->get<AllocatedData>(Sym))
454 if (NContext == LeakContext ||
463std::unique_ptr<PathSensitiveBugReport>
464MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
465 const AllocationPair &AP, ExplodedNode *N, CheckerContext &
C)
const {
466 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
467 SmallString<70> sbuf;
468 llvm::raw_svector_ostream os(sbuf);
469 os <<
"Allocated data is not released: missing a call to '"
470 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
475 PathDiagnosticLocation LocUsedForUniqueing;
476 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first,
C);
481 C.getSourceManager(),
484 auto Report = std::make_unique<PathSensitiveBugReport>(
485 BT, os.str(), N, LocUsedForUniqueing,
488 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
489 markInteresting(
Report.get(), AP);
497 bool Assumption)
const {
498 AllocatedDataTy AMap = State->get<AllocatedData>();
502 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(
Cond.getAsSymbol());
506 if (OpCode != BO_EQ && OpCode != BO_NE)
512 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
513 const llvm::APInt &RHS = SIE->getRHS();
514 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
515 (OpCode == BO_NE && RHS == NoErr);
517 ErrorIsReturned = !ErrorIsReturned;
519 ReturnSymbol = SIE->getLHS();
523 for (
auto [Sym, AllocState] : AMap) {
524 if (ReturnSymbol == AllocState.Region)
525 State = State->remove<AllocatedData>(Sym);
531void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
532 CheckerContext &
C)
const {
534 AllocatedDataTy AMap = State->get<AllocatedData>();
539 AllocationPairVec Errors;
540 for (
const auto &[Sym, AllocState] : AMap) {
545 State = State->remove<AllocatedData>(Sym);
547 ConstraintManager &CMgr = State->getConstraintManager();
548 ConditionTruthVal AllocFailed = CMgr.isNull(State, Sym);
551 Errors.push_back(std::make_pair(Sym, &AllocState));
555 C.addTransition(State);
559 ExplodedNode *N =
C.generateNonFatalErrorNode(
C.getState());
564 for (
const auto &P : Errors)
565 C.emitReport(generateAllocatedDataNotReleasedReport(P, N,
C));
568 C.addTransition(State, N);
579 for (
auto I : State->get<AllocatedData>()) {
581 if (Escaped.count(Sym))
582 State = State->remove<AllocatedData>(Sym);
600 if (
const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
601 SymbolRef ParentSym = SD->getParentSymbol();
602 if (Escaped.count(ParentSym))
603 State = State->remove<AllocatedData>(Sym);
610MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
611 const ExplodedNode *N, BugReporterContext &BRC,
612 PathSensitiveBugReport &BR) {
613 const AllocationState *AS = N->
getState()->get<AllocatedData>(Sym);
616 const AllocationState *ASPrev =
626 assert(funDecl &&
"We do not support indirect function calls as of now.");
627 StringRef funName = funDecl->
getName();
630 unsigned Idx = getTrackedFunctionIndex(funName,
true);
631 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
632 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
635 return std::make_shared<PathDiagnosticEventPiece>(Pos,
636 "Data is allocated here.");
639void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
642 const char *Sep)
const {
644 AllocatedDataTy AMap = State->get<AllocatedData>();
646 if (!AMap.isEmpty()) {
647 Out << Sep <<
"KeychainAPIChecker :" << NL;
648 for (
SymbolRef Sym : llvm::make_first_range(AMap)) {
655void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
659bool ento::shouldRegisterMacOSKeychainAPIChecker(
const CheckerManager &mgr) {
static bool isBadDeallocationArgument(const MemRegion *Arg)
static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)
Given the address expression, retrieve the value it's pointing to.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
BinaryOperatorKind Opcode
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
A reference to a declared variable, function, enum, etc.
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
@ NPC_ValueDependentIsNotNull
Specifies that a value-dependent expression should be considered to never be a null pointer constant.
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant.
bool isParentOf(const LocationContext *LC) const
const Decl * getDecl() const
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
const SourceManager & getSourceManager() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
const ProgramStateRef & getState() const
pred_iterator pred_begin()
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
const MemRegion * getAsRegion() const
virtual void dumpToStream(raw_ostream &os) const
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
const char *const AppleAPIMisuse
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
U cast(CodeGen::Address addr)