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>,
33 mutable std::unique_ptr<BugType> BT;
38 struct AllocationState {
40 unsigned int AllocatorIdx;
43 AllocationState(
const Expr *E,
unsigned int Idx,
SymbolRef R) :
48 return (AllocatorIdx == X.AllocatorIdx &&
52 void Profile(llvm::FoldingSetNodeID &
ID)
const {
53 ID.AddInteger(AllocatorIdx);
54 ID.AddPointer(Region);
62 bool Assumption)
const;
64 const char *NL,
const char *Sep)
const;
67 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
81 struct ADFunctionInfo {
84 unsigned int DeallocatorIdx;
87 static const unsigned InvalidIdx = 100000;
88 static const unsigned FunctionsToTrackSize = 8;
89 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
91 static const unsigned NoErr = 0;
95 static unsigned getTrackedFunctionIndex(StringRef Name,
bool IsAllocator);
97 inline void initBugType()
const {
99 BT.reset(
new BugType(
this,
"Improper use of SecKeychain API",
100 "API Misuse (Apple)"));
103 void generateDeallocatorMismatchReport(
const AllocationPair &AP,
111 std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport(
115 void markInteresting(
BugReport *R,
const AllocationPair &AP)
const {
123 class SecKeychainBugVisitor
130 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
132 void Profile(llvm::FoldingSetNodeID &ID)
const override {
138 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
151 MacOSKeychainAPIChecker::AllocationState)
153 static bool isEnclosingFunctionParam(
const Expr *E) {
155 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
157 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
163 const 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},
175 unsigned 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))
196 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
197 isa<TypedRegion>(Arg);
218 void MacOSKeychainAPIChecker::
219 generateDeallocatorMismatchReport(
const AllocationPair &AP,
223 State = State->remove<AllocatedData>(AP.first);
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 = llvm::make_unique<BugReport>(*BT, os.str(), N);
237 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
239 markInteresting(Report.get(), AP);
243 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
245 unsigned idx = InvalidIdx;
249 if (!FD || FD->
getKind() != Decl::Function)
257 idx = getTrackedFunctionIndex(funName,
true);
258 if (idx != InvalidIdx) {
259 unsigned paramIdx = FunctionsToTrack[idx].Param;
265 if (
const AllocationState *AS = State->get<AllocatedData>(V)) {
268 State = State->remove<AllocatedData>(V);
274 llvm::raw_svector_ostream os(sbuf);
275 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
276 os <<
"Allocated data should be released before another call to " 277 <<
"the allocator: missing a call to '" 278 << FunctionsToTrack[DIdx].Name
280 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
281 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
283 Report->markInteresting(AS->Region);
290 idx = getTrackedFunctionIndex(funName,
false);
291 if (idx == InvalidIdx)
294 unsigned paramIdx = FunctionsToTrack[idx].Param;
310 bool RegionArgIsBad =
false;
314 RegionArgIsBad =
true;
318 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
324 if (RegionArgIsBad) {
327 if (isEnclosingFunctionParam(ArgExpr))
334 auto Report = llvm::make_unique<BugReport>(
335 *BT,
"Trying to free data which has not been allocated.", N);
338 Report->markInteresting(AS->Region);
344 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
346 if (funName ==
"CFStringCreateWithBytesNoCopy") {
351 const AllocationPair AP = std::make_pair(ArgSM, AS);
352 generateDeallocatorMismatchReport(AP, ArgExpr, C);
356 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
357 StringRef DeallocatorName = DE->getFoundDecl()->getName();
358 if (DeallocatorName ==
"kCFAllocatorDefault" ||
359 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
360 DeallocatorName ==
"kCFAllocatorMalloc") {
361 const AllocationPair AP = std::make_pair(ArgSM, AS);
362 generateDeallocatorMismatchReport(AP, ArgExpr, C);
367 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
372 State = State->remove<AllocatedData>(ArgSM);
377 llvm_unreachable(
"We know of no other possible APIs.");
382 State = State->remove<AllocatedData>(ArgSM);
385 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
386 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
387 const AllocationPair AP = std::make_pair(ArgSM, AS);
388 generateDeallocatorMismatchReport(AP, ArgExpr, C);
395 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
399 if (!FD || FD->
getKind() != Decl::Function)
405 unsigned idx = getTrackedFunctionIndex(funName,
true);
406 if (idx == InvalidIdx)
409 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
412 if (isEnclosingFunctionParam(ArgExpr) &&
434 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
443 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
452 if (!N->
getState()->get<AllocatedData>(Sym))
457 if (NContext == LeakContext ||
466 std::unique_ptr<BugReport>
467 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
469 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
472 llvm::raw_svector_ostream os(sbuf);
473 os <<
"Allocated data is not released: missing a call to '" 474 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
480 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
489 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
492 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
493 markInteresting(Report.get(), AP);
501 bool Assumption)
const {
502 AllocatedDataTy AMap = State->get<AllocatedData>();
506 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymExpr());
510 if (OpCode != BO_EQ && OpCode != BO_NE)
516 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
517 const llvm::APInt &RHS = SIE->getRHS();
518 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
519 (OpCode == BO_NE && RHS == NoErr);
521 ErrorIsReturned = !ErrorIsReturned;
523 ReturnSymbol = SIE->getLHS();
527 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
528 if (ReturnSymbol == I->second.Region)
529 State = State->remove<AllocatedData>(I->first);
535 void MacOSKeychainAPIChecker::checkDeadSymbols(
SymbolReaper &SR,
538 AllocatedDataTy AMap = State->get<AllocatedData>();
542 bool Changed =
false;
543 AllocationPairVec Errors;
544 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
549 State = State->remove<AllocatedData>(I->first);
555 Errors.push_back(std::make_pair(I->first, &I->second));
569 for (
const auto &
P : Errors)
570 C.
emitReport(generateAllocatedDataNotReleasedReport(
P, N, C));
576 std::shared_ptr<PathDiagnosticPiece>
577 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
580 const AllocationState *AS = N->
getState()->get<AllocatedData>(Sym);
583 const AllocationState *ASPrev = PrevN->
getState()->get<AllocatedData>(Sym);
592 assert(funDecl &&
"We do not support indirect function calls as of now.");
593 StringRef funName = funDecl->getName();
596 unsigned Idx = getTrackedFunctionIndex(funName,
true);
597 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
598 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
601 return std::make_shared<PathDiagnosticEventPiece>(Pos,
602 "Data is allocated here.");
605 void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
608 const char *Sep)
const {
610 AllocatedDataTy AMap = State->get<AllocatedData>();
612 if (!AMap.isEmpty()) {
613 Out << Sep <<
"KeychainAPIChecker :" << NL;
614 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
615 I.getKey()->dumpToStream(Out);
An instance of this class is created to represent a function declaration or definition.
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)
Stmt - This represents one statement.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
const ProgramStateRef & getState() const
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
const FunctionDecl * getCalleeDecl(const CallExpr *CE) const
Get the declaration of the called function (path-sensitive).
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
virtual SVal getBinding(Store store, Loc loc, QualType T=QualType())=0
Return the value bound to specified location in a given state.
bool isParentOf(const LocationContext *LC) const
StringRef getCalleeName(const FunctionDecl *FunDecl) const
Get the name of the called function (path-sensitive).
static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)
Given the address expression, retrieve the value it's pointing to.
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent)
Add artificial symbol dependency.
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
const LocationContext * getLocationContext() const
const LocationContext * getParent() const
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
ValueDecl - Represent the declaration of a variable (in which case it is an lvalue) a function (in wh...
Expr - This represents one expression.
SymbolManager & getSymbolManager()
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.
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
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)
CHECKER * registerChecker()
Used to register checkers.
StoreManager & getStoreManager()
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant...
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type...
const MemRegion * getAsRegion() const
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Specifies that a value-dependent expression should be considered to never be a null pointer constant...
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. ...
Dataflow Directional Tag Classes.
ASTContext & getASTContext()
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
const Decl * getDecl() const
const ProgramStateRef & getState() const
const SymExpr * getAsSymExpr() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
pred_iterator pred_begin()
SourceManager & getSourceManager()
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.
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...
const LocationContext * getLocationContext() const
SourceManager & getSourceManager()