28class MacOSKeychainAPIChecker :
public Checker<check::PreStmt<CallExpr>,
29 check::PostStmt<CallExpr>,
33 const BugType BT{
this,
"Improper use of SecKeychain API",
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;
73 typedef SmallVector<AllocationPair, 2> AllocationPairVec;
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 void generateDeallocatorMismatchReport(
const AllocationPair &AP,
104 CheckerContext &
C)
const;
107 const ExplodedNode *getAllocationNode(
const ExplodedNode *N,
SymbolRef Sym,
108 CheckerContext &
C)
const;
110 std::unique_ptr<PathSensitiveBugReport>
111 generateAllocatedDataNotReleasedReport(
const AllocationPair &AP,
113 CheckerContext &
C)
const;
116 void markInteresting(PathSensitiveBugReport *R,
117 const AllocationPair &AP)
const {
118 R->markInteresting(AP.first);
119 R->markInteresting(AP.second->Region);
125 class SecKeychainBugVisitor :
public BugReporterVisitor {
131 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
133 void Profile(llvm::FoldingSetNodeID &ID)
const override {
140 BugReporterContext &BRC,
141 PathSensitiveBugReport &BR)
override;
151 MacOSKeychainAPIChecker::AllocationState)
153static bool isEnclosingFunctionParam(
const Expr *E) {
155 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
163const MacOSKeychainAPIChecker::ADFunctionInfo
164 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
165 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
166 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
167 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
168 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
169 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
170 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
171 {
"free", 0, InvalidIdx, ErrorAPI},
172 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
175unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
177 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
178 ADFunctionInfo FI = FunctionsToTrack[I];
182 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
184 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
208 SymbolRef sym =
SM.getBinding(State->getStore(), *
X).getAsLocSymbol();
217void MacOSKeychainAPIChecker::
218 generateDeallocatorMismatchReport(
const AllocationPair &AP,
220 CheckerContext &
C)
const {
222 State = State->remove<AllocatedData>(AP.first);
223 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
227 unsigned int PDeallocIdx =
228 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
230 auto Report = std::make_unique<PathSensitiveBugReport>(
232 "Deallocator doesn't match the allocator: '" +
233 Twine(FunctionsToTrack[PDeallocIdx].Name) +
"' should be used.",
235 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
237 markInteresting(
Report.get(), AP);
238 C.emitReport(std::move(
Report));
241void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
242 CheckerContext &
C)
const {
243 unsigned idx = InvalidIdx;
246 const FunctionDecl *FD =
C.getCalleeDecl(CE);
247 if (!FD || FD->
getKind() != Decl::Function)
250 StringRef funName =
C.getCalleeName(FD);
255 idx = getTrackedFunctionIndex(funName,
true);
256 if (idx != InvalidIdx) {
257 unsigned paramIdx = FunctionsToTrack[idx].Param;
261 const Expr *ArgExpr = CE->
getArg(paramIdx);
263 if (
const AllocationState *AS = State->get<AllocatedData>(
V)) {
266 State = State->remove<AllocatedData>(
V);
267 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
270 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
271 auto Report = std::make_unique<PathSensitiveBugReport>(
273 "Allocated data should be released before another call to "
274 "the allocator: missing a call to '" +
275 Twine(FunctionsToTrack[DIdx].Name) +
"'.",
277 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(
V));
279 Report->markInteresting(AS->Region);
280 C.emitReport(std::move(
Report));
286 idx = getTrackedFunctionIndex(funName,
false);
287 if (idx == InvalidIdx)
290 unsigned paramIdx = FunctionsToTrack[idx].Param;
295 const Expr *ArgExpr = CE->
getArg(paramIdx);
296 SVal ArgSVal =
C.getSVal(ArgExpr);
306 bool RegionArgIsBad =
false;
310 RegionArgIsBad =
true;
314 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
320 if (RegionArgIsBad) {
323 if (isEnclosingFunctionParam(ArgExpr))
326 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
329 auto Report = std::make_unique<PathSensitiveBugReport>(
330 BT,
"Trying to free data which has not been allocated.", N);
333 Report->markInteresting(AS->Region);
334 C.emitReport(std::move(
Report));
339 if (FunctionsToTrack[idx].Kind == PossibleAPI) {
341 if (funName ==
"CFStringCreateWithBytesNoCopy") {
346 const AllocationPair AP = std::make_pair(ArgSM, AS);
347 generateDeallocatorMismatchReport(AP, ArgExpr,
C);
351 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
352 StringRef DeallocatorName = DE->getFoundDecl()->getName();
353 if (DeallocatorName ==
"kCFAllocatorDefault" ||
354 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
355 DeallocatorName ==
"kCFAllocatorMalloc") {
356 const AllocationPair AP = std::make_pair(ArgSM, AS);
357 generateDeallocatorMismatchReport(AP, ArgExpr,
C);
362 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
367 State = State->remove<AllocatedData>(ArgSM);
368 C.addTransition(State);
372 llvm_unreachable(
"We know of no other possible APIs.");
377 State = State->remove<AllocatedData>(ArgSM);
380 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
381 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
382 const AllocationPair AP = std::make_pair(ArgSM, AS);
383 generateDeallocatorMismatchReport(AP, ArgExpr,
C);
387 C.addTransition(State);
390void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
391 CheckerContext &
C)
const {
393 const FunctionDecl *FD =
C.getCalleeDecl(CE);
394 if (!FD || FD->
getKind() != Decl::Function)
397 StringRef funName =
C.getCalleeName(FD);
400 unsigned idx = getTrackedFunctionIndex(funName,
true);
401 if (idx == InvalidIdx)
404 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
407 if (isEnclosingFunctionParam(ArgExpr) &&
408 C.getStackFrame()->getParent() ==
nullptr)
424 SymbolRef RetStatusSymbol =
C.getSVal(CE).getAsSymbol();
425 C.getSymbolManager().addSymbolDependency(
V, RetStatusSymbol);
428 State = State->set<AllocatedData>(
V, AllocationState(ArgExpr, idx,
431 C.addTransition(State);
437MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
439 CheckerContext &
C)
const {
443 const ExplodedNode *AllocNode = N;
446 if (!N->
getState()->get<AllocatedData>(Sym))
451 if (NSF == LeakStackFrame || NSF->
isParentOf(LeakStackFrame))
459std::unique_ptr<PathSensitiveBugReport>
460MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
461 const AllocationPair &AP, ExplodedNode *N, CheckerContext &
C)
const {
462 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
467 PathDiagnosticLocation LocUsedForUniqueing;
468 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first,
C);
475 auto Report = std::make_unique<PathSensitiveBugReport>(
477 "Allocated data is not released: missing a call to '" +
478 Twine(FunctionsToTrack[FI.DeallocatorIdx].Name) +
"'.",
481 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
482 markInteresting(
Report.get(), AP);
490 bool Assumption)
const {
491 AllocatedDataTy AMap = State->get<AllocatedData>();
495 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(
Cond.getAsSymbol());
499 if (OpCode != BO_EQ && OpCode != BO_NE)
505 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
506 const llvm::APInt &RHS = SIE->getRHS();
507 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
508 (OpCode == BO_NE && RHS == NoErr);
510 ErrorIsReturned = !ErrorIsReturned;
512 ReturnSymbol = SIE->getLHS();
516 for (
auto [Sym, AllocState] : AMap) {
517 if (ReturnSymbol == AllocState.Region)
518 State = State->remove<AllocatedData>(Sym);
524void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
525 CheckerContext &
C)
const {
527 AllocatedDataTy AMap = State->get<AllocatedData>();
532 AllocationPairVec Errors;
533 for (
const auto &[Sym, AllocState] : AMap) {
538 State = State->remove<AllocatedData>(Sym);
540 ConstraintManager &CMgr = State->getConstraintManager();
541 ConditionTruthVal AllocFailed = CMgr.isNull(State, Sym);
544 Errors.push_back(std::make_pair(Sym, &AllocState));
548 C.addTransition(State);
552 ExplodedNode *N =
C.generateNonFatalErrorNode(
C.getState());
557 for (
const auto &P : Errors)
558 C.emitReport(generateAllocatedDataNotReleasedReport(P, N,
C));
561 C.addTransition(State, N);
572 for (
auto I : State->get<AllocatedData>()) {
574 if (Escaped.count(Sym))
575 State = State->remove<AllocatedData>(Sym);
593 if (
const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
594 SymbolRef ParentSym = SD->getParentSymbol();
595 if (Escaped.count(ParentSym))
596 State = State->remove<AllocatedData>(Sym);
603MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
604 const ExplodedNode *N, BugReporterContext &BRC,
605 PathSensitiveBugReport &BR) {
606 const AllocationState *AS = N->
getState()->get<AllocatedData>(Sym);
609 const AllocationState *ASPrev =
619 assert(funDecl &&
"We do not support indirect function calls as of now.");
620 StringRef funName = funDecl->
getName();
623 unsigned Idx = getTrackedFunctionIndex(funName,
true);
624 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
625 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
628 return std::make_shared<PathDiagnosticEventPiece>(Pos,
629 "Data is allocated here.");
632void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
635 const char *Sep)
const {
637 AllocatedDataTy AMap = State->get<AllocatedData>();
639 if (!AMap.isEmpty()) {
640 Out << Sep <<
"KeychainAPIChecker :" << NL;
641 for (
SymbolRef Sym : llvm::make_first_range(AMap)) {
648void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
652bool 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.
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.
bool isParentOf(const StackFrame *SF) const
const Decl * getDecl() const
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.
ExplodedNode * getFirstPred()
const StackFrame * getStackFrame() const
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.
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)