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>,
32 mutable std::unique_ptr<BugType> BT;
37 struct AllocationState {
39 unsigned int AllocatorIdx;
47 return (AllocatorIdx == X.AllocatorIdx &&
51 void Profile(llvm::FoldingSetNodeID &
ID)
const {
52 ID.AddInteger(AllocatorIdx);
53 ID.AddPointer(Region);
62 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
76 struct ADFunctionInfo {
79 unsigned int DeallocatorIdx;
82 static const unsigned InvalidIdx = 100000;
83 static const unsigned FunctionsToTrackSize = 8;
84 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
86 static const unsigned NoErr = 0;
90 static unsigned getTrackedFunctionIndex(StringRef
Name,
bool IsAllocator);
92 inline void initBugType()
const {
94 BT.reset(
new BugType(
this,
"Improper use of SecKeychain API",
95 "API Misuse (Apple)"));
98 void generateDeallocatorMismatchReport(
const AllocationPair &AP,
106 std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport(
110 bool definitelyReturnedError(
SymbolRef RetSym,
113 bool noError =
false)
const;
116 bool definitelyDidnotReturnError(
SymbolRef RetSym,
119 return definitelyReturnedError(RetSym, State, Builder,
true);
123 void markInteresting(
BugReport *R,
const AllocationPair &AP)
const {
131 class SecKeychainBugVisitor
138 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
140 void Profile(llvm::FoldingSetNodeID &ID)
const override {
159 MacOSKeychainAPIChecker::AllocationState)
161 static
bool isEnclosingFunctionParam(const
Expr *
E) {
162 E = E->IgnoreParenCasts();
163 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
165 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
171 const MacOSKeychainAPIChecker::ADFunctionInfo
172 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
173 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
174 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
175 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
176 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
177 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
178 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
179 {
"free", 0, InvalidIdx, ErrorAPI},
180 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
183 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef
Name,
185 for (
unsigned I = 0;
I < FunctionsToTrackSize; ++
I) {
186 ADFunctionInfo FI = FunctionsToTrack[
I];
190 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
192 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
204 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
205 isa<TypedRegion>(Arg);
230 bool MacOSKeychainAPIChecker::definitelyReturnedError(
SymbolRef RetSym,
233 bool noError)
const {
239 return ErrState ==
State;
244 void MacOSKeychainAPIChecker::
245 generateDeallocatorMismatchReport(
const AllocationPair &AP,
249 State = State->remove<AllocatedData>(AP.first);
256 llvm::raw_svector_ostream os(sbuf);
257 unsigned int PDeallocIdx =
258 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
260 os <<
"Deallocator doesn't match the allocator: '"
261 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
262 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
263 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
264 Report->addRange(ArgExpr->getSourceRange());
265 markInteresting(Report.get(), AP);
269 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
271 unsigned idx = InvalidIdx;
275 if (!FD || FD->getKind() != Decl::Function)
283 idx = getTrackedFunctionIndex(funName,
true);
284 if (idx != InvalidIdx) {
285 unsigned paramIdx = FunctionsToTrack[idx].Param;
291 if (
const AllocationState *AS = State->get<AllocatedData>(V)) {
292 if (!definitelyReturnedError(AS->Region, State, C.
getSValBuilder())) {
295 State = State->remove<AllocatedData>(V);
301 llvm::raw_svector_ostream os(sbuf);
302 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
303 os <<
"Allocated data should be released before another call to "
304 <<
"the allocator: missing a call to '"
305 << FunctionsToTrack[DIdx].Name
307 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
308 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
309 Report->addRange(ArgExpr->getSourceRange());
310 Report->markInteresting(AS->Region);
318 idx = getTrackedFunctionIndex(funName,
false);
319 if (idx == InvalidIdx)
322 unsigned paramIdx = FunctionsToTrack[idx].Param;
338 bool RegionArgIsBad =
false;
342 RegionArgIsBad =
true;
346 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
347 if (!AS && FunctionsToTrack[idx].
Kind != ValidAPI) {
353 if (!AS || RegionArgIsBad) {
356 if (isEnclosingFunctionParam(ArgExpr))
363 auto Report = llvm::make_unique<BugReport>(
364 *BT,
"Trying to free data which has not been allocated.", N);
365 Report->addRange(ArgExpr->getSourceRange());
367 Report->markInteresting(AS->Region);
373 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
375 if (funName ==
"CFStringCreateWithBytesNoCopy") {
380 const AllocationPair AP = std::make_pair(ArgSM, AS);
381 generateDeallocatorMismatchReport(AP, ArgExpr, C);
385 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
386 StringRef DeallocatorName = DE->getFoundDecl()->getName();
387 if (DeallocatorName ==
"kCFAllocatorDefault" ||
388 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
389 DeallocatorName ==
"kCFAllocatorMalloc") {
390 const AllocationPair AP = std::make_pair(ArgSM, AS);
391 generateDeallocatorMismatchReport(AP, ArgExpr, C);
396 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
401 State = State->remove<AllocatedData>(ArgSM);
406 llvm_unreachable(
"We know of no other possible APIs.");
411 State = State->remove<AllocatedData>(ArgSM);
414 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
415 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
416 const AllocationPair AP = std::make_pair(ArgSM, AS);
417 generateDeallocatorMismatchReport(AP, ArgExpr, C);
424 !definitelyDidnotReturnError(AS->Region, State, C.
getSValBuilder())) {
429 auto Report = llvm::make_unique<BugReport>(
430 *BT,
"Only call free if a valid (non-NULL) buffer was returned.", N);
431 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM));
432 Report->addRange(ArgExpr->getSourceRange());
433 Report->markInteresting(AS->Region);
441 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
445 if (!FD || FD->getKind() != Decl::Function)
451 unsigned idx = getTrackedFunctionIndex(funName,
true);
452 if (idx == InvalidIdx)
455 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
458 if (isEnclosingFunctionParam(ArgExpr) &&
480 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
489 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
498 if (!N->
getState()->get<AllocatedData>(Sym))
503 if (NContext == LeakContext ||
512 std::unique_ptr<BugReport>
513 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
515 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
518 llvm::raw_svector_ostream os(sbuf);
519 os <<
"Allocated data is not released: missing a call to '"
520 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
526 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
527 const Stmt *AllocStmt =
nullptr;
530 AllocStmt = Exit->getCalleeContext()->getCallSite();
532 AllocStmt = PS->getStmt();
540 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
543 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
544 markInteresting(Report.get(), AP);
548 void MacOSKeychainAPIChecker::checkDeadSymbols(
SymbolReaper &SR,
551 AllocatedDataTy ASet = State->get<AllocatedData>();
555 bool Changed =
false;
556 AllocationPairVec Errors;
562 State = State->remove<AllocatedData>(
I->first);
570 Errors.push_back(std::make_pair(
I->first, &
I->second));
584 for (
const auto &P : Errors)
585 C.
emitReport(generateAllocatedDataNotReleasedReport(P, N, C));
597 const AllocationState *AS = N->
getState()->get<AllocatedData>(Sym);
600 const AllocationState *ASPrev = PrevN->
getState()->get<AllocatedData>(Sym);
609 assert(funDecl &&
"We do not support indirect function calls as of now.");
610 StringRef funName = funDecl->getName();
613 unsigned Idx = getTrackedFunctionIndex(funName,
true);
614 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
615 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
StringRef getCalleeName(const FunctionDecl *FunDecl) const
Get the name of the called function (path-sensitive).
SymbolManager & getSymbolManager()
MemRegion - The root abstract class for all memory regions.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
bool operator==(CanQual< T > x, CanQual< U > y)
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
virtual SVal getBinding(Store store, Loc loc, QualType T=QualType())=0
Return the value bound to specified location in a given state.
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant...
static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)
Given the address expression, retrieve the value it's pointing to.
class LLVM_ALIGNAS(8) DependentTemplateSpecializationType const IdentifierInfo * Name
Represents a template specialization type whose template cannot be resolved, e.g. ...
const FunctionDecl * getCalleeDecl(const CallExpr *CE) const
Get the declaration of the called function (path-sensitive).
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent)
Add artificial symbol dependency.
bool isParentOf(const LocationContext *LC) const
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
detail::InMemoryDirectory::const_iterator I
const LocationContext * getLocationContext() const
QualType getType(const SymExpr *SE) const
Represents a point when we finish the call exit sequence (for inlined call).
ValueDecl - Represent the declaration of a variable (in which case it is an lvalue) a function (in wh...
Expr - This represents one expression.
const ProgramStateRef & getState() const
const ProgramStateRef & getState() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
SymbolManager & getSymbolManager()
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type...
void markInteresting(SymbolRef sym)
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static bool isBadDeallocationArgument(const MemRegion *Arg)
CHECKER * registerChecker()
Used to register checkers.
StoreManager & getStoreManager()
const TemplateArgument * iterator
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx)
Specifies that a value-dependent expression should be considered to never be a null pointer constant...
const Decl * getDecl() const
A class responsible for cleaning up unused symbols.
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. ...
const LocationContext * getParent() const
ASTContext & getASTContext()
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Represents symbolic expression.
detail::InMemoryDirectory::const_iterator E
const MemRegion * getAsRegion() const
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
BoundNodesTreeBuilder *const Builder
DefinedOrUnknownSVal evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs)
pred_iterator pred_begin()
SourceManager & getSourceManager()
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
A reference to a declared variable, function, enum, etc.
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
This class provides an interface through which checkers can create individual bug reports...
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
SourceManager & getSourceManager()
bool isLive(SymbolRef sym)
const LocationContext * getLocationContext() const