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;
42 AllocationState(
const Expr *E,
unsigned int Idx,
SymbolRef R) :
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) {}
139 ~SecKeychainBugVisitor()
override {}
141 void Profile(llvm::FoldingSetNodeID &ID)
const override {
160 MacOSKeychainAPIChecker::AllocationState)
162 static
bool isEnclosingFunctionParam(const
Expr *E) {
163 E = E->IgnoreParenCasts();
164 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
166 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
172 const MacOSKeychainAPIChecker::ADFunctionInfo
173 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
174 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
175 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
176 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
177 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
178 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
179 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
180 {
"free", 0, InvalidIdx, ErrorAPI},
181 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
184 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
186 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
187 ADFunctionInfo FI = FunctionsToTrack[I];
191 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
193 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
205 if (isa<AllocaRegion>(Arg) ||
206 isa<BlockDataRegion>(Arg) ||
207 isa<TypedRegion>(Arg)) {
235 bool MacOSKeychainAPIChecker::definitelyReturnedError(
SymbolRef RetSym,
238 bool noError)
const {
244 if (ErrState == State) {
253 void MacOSKeychainAPIChecker::
254 generateDeallocatorMismatchReport(
const AllocationPair &AP,
258 State = State->remove<AllocatedData>(AP.first);
265 llvm::raw_svector_ostream os(sbuf);
266 unsigned int PDeallocIdx =
267 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
269 os <<
"Deallocator doesn't match the allocator: '"
270 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
271 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
272 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
273 Report->addRange(ArgExpr->getSourceRange());
274 markInteresting(Report.get(), AP);
278 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
280 unsigned idx = InvalidIdx;
284 if (!FD || FD->
getKind() != Decl::Function)
292 idx = getTrackedFunctionIndex(funName,
true);
293 if (idx != InvalidIdx) {
294 unsigned paramIdx = FunctionsToTrack[idx].Param;
300 if (
const AllocationState *AS = State->get<AllocatedData>(V)) {
301 if (!definitelyReturnedError(AS->Region, State, C.
getSValBuilder())) {
304 State = State->remove<AllocatedData>(V);
310 llvm::raw_svector_ostream os(sbuf);
311 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
312 os <<
"Allocated data should be released before another call to "
313 <<
"the allocator: missing a call to '"
314 << FunctionsToTrack[DIdx].Name
316 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
317 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
318 Report->addRange(ArgExpr->getSourceRange());
319 Report->markInteresting(AS->Region);
327 idx = getTrackedFunctionIndex(funName,
false);
328 if (idx == InvalidIdx)
331 unsigned paramIdx = FunctionsToTrack[idx].Param;
347 bool RegionArgIsBad =
false;
351 RegionArgIsBad =
true;
355 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
356 if (!AS && FunctionsToTrack[idx].
Kind != ValidAPI) {
362 if (!AS || RegionArgIsBad) {
365 if (isEnclosingFunctionParam(ArgExpr))
372 auto Report = llvm::make_unique<BugReport>(
373 *BT,
"Trying to free data which has not been allocated.", N);
374 Report->addRange(ArgExpr->getSourceRange());
376 Report->markInteresting(AS->Region);
382 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
384 if (funName ==
"CFStringCreateWithBytesNoCopy") {
389 const AllocationPair AP = std::make_pair(ArgSM, AS);
390 generateDeallocatorMismatchReport(AP, ArgExpr, C);
394 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
395 StringRef DeallocatorName = DE->getFoundDecl()->getName();
396 if (DeallocatorName ==
"kCFAllocatorDefault" ||
397 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
398 DeallocatorName ==
"kCFAllocatorMalloc") {
399 const AllocationPair AP = std::make_pair(ArgSM, AS);
400 generateDeallocatorMismatchReport(AP, ArgExpr, C);
405 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
410 State = State->remove<AllocatedData>(ArgSM);
415 llvm_unreachable(
"We know of no other possible APIs.");
420 State = State->remove<AllocatedData>(ArgSM);
423 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
424 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
425 const AllocationPair AP = std::make_pair(ArgSM, AS);
426 generateDeallocatorMismatchReport(AP, ArgExpr, C);
433 !definitelyDidnotReturnError(AS->Region, State, C.
getSValBuilder())) {
438 auto Report = llvm::make_unique<BugReport>(
439 *BT,
"Only call free if a valid (non-NULL) buffer was returned.", N);
440 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM));
441 Report->addRange(ArgExpr->getSourceRange());
442 Report->markInteresting(AS->Region);
450 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
454 if (!FD || FD->
getKind() != Decl::Function)
460 unsigned idx = getTrackedFunctionIndex(funName,
true);
461 if (idx == InvalidIdx)
464 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
467 if (isEnclosingFunctionParam(ArgExpr) &&
489 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
498 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
507 if (!N->
getState()->get<AllocatedData>(Sym))
512 if (NContext == LeakContext ||
521 std::unique_ptr<BugReport>
522 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
524 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
527 llvm::raw_svector_ostream os(sbuf);
528 os <<
"Allocated data is not released: missing a call to '"
529 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
535 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
536 const Stmt *AllocStmt =
nullptr;
539 AllocStmt = Exit->getCalleeContext()->getCallSite();
541 AllocStmt = PS->getStmt();
549 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
552 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
553 markInteresting(Report.get(), AP);
557 void MacOSKeychainAPIChecker::checkDeadSymbols(
SymbolReaper &SR,
560 AllocatedDataTy ASet = State->get<AllocatedData>();
564 bool Changed =
false;
565 AllocationPairVec Errors;
566 for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
571 State = State->remove<AllocatedData>(I->first);
577 definitelyReturnedError(I->second.Region, State, C.
getSValBuilder()))
579 Errors.push_back(std::make_pair(I->first, &I->second));
591 for (
const auto P : Errors)
592 C.
emitReport(generateAllocatedDataNotReleasedReport(P, N, C));
604 const AllocationState *AS = N->
getState()->get<AllocatedData>(Sym);
607 const AllocationState *ASPrev = PrevN->
getState()->get<AllocatedData>(Sym);
616 assert(funDecl &&
"We do not support indirect function calls as of now.");
617 StringRef funName = funDecl->getName();
620 unsigned Idx = getTrackedFunctionIndex(funName,
true);
621 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
622 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
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). Uses the default CheckerContex...
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
virtual SVal getBinding(Store store, Loc loc, QualType T=QualType())=0
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
Symbolic value. These values used to capture symbolic execution of the program.
static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)
const FunctionDecl * getCalleeDecl(const CallExpr *CE) const
Get the declaration of the called function (path-sensitive).
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef. Otherwise return 0...
void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent)
Add artificial symbol dependency.
bool isParentOf(const LocationContext *LC) const
Expr * IgnoreParenCasts() LLVM_READONLY
const LocationContext * getLocationContext() const
QualType getType(const SymExpr *SE) const
ID
Defines the set of possible language-specific address spaces.
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)
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()
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)
const LocationContext * getParent() const
ASTContext & getASTContext()
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Represents symbolic expression.
const MemRegion * getAsRegion() const
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
unsigned getNumArgs() const
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
BoundNodesTreeBuilder *const Builder
DefinedOrUnknownSVal evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs)
pred_iterator pred_begin()
SourceManager & getSourceManager()
SValBuilder & getSValBuilder()
A reference to a declared variable, function, enum, etc. [C99 6.5.1p2].
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