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 {
129 SecKeychainBugVisitor(
SymbolRef S) : Sym(S) {}
131 void Profile(llvm::FoldingSetNodeID &ID)
const override {
137 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
150 MacOSKeychainAPIChecker::AllocationState)
152 static bool isEnclosingFunctionParam(
const Expr *E) {
154 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
156 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
162 const MacOSKeychainAPIChecker::ADFunctionInfo
163 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
164 {
"SecKeychainItemCopyContent", 4, 3, ValidAPI},
165 {
"SecKeychainFindGenericPassword", 6, 3, ValidAPI},
166 {
"SecKeychainFindInternetPassword", 13, 3, ValidAPI},
167 {
"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},
168 {
"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},
169 {
"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},
170 {
"free", 0, InvalidIdx, ErrorAPI},
171 {
"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},
174 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
176 for (
unsigned I = 0; I < FunctionsToTrackSize; ++I) {
177 ADFunctionInfo FI = FunctionsToTrack[I];
181 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
183 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
195 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
196 isa<TypedRegion>(Arg);
217 void MacOSKeychainAPIChecker::
218 generateDeallocatorMismatchReport(
const AllocationPair &AP,
222 State = State->remove<AllocatedData>(AP.first);
229 llvm::raw_svector_ostream os(sbuf);
230 unsigned int PDeallocIdx =
231 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
233 os <<
"Deallocator doesn't match the allocator: '" 234 << FunctionsToTrack[PDeallocIdx].Name <<
"' should be used.";
235 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
236 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
238 markInteresting(Report.get(), AP);
242 void MacOSKeychainAPIChecker::checkPreStmt(
const CallExpr *CE,
244 unsigned idx = InvalidIdx;
256 idx = getTrackedFunctionIndex(funName,
true);
257 if (idx != InvalidIdx) {
258 unsigned paramIdx = FunctionsToTrack[idx].Param;
264 if (
const AllocationState *AS = State->get<AllocatedData>(V)) {
267 State = State->remove<AllocatedData>(V);
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 = llvm::make_unique<BugReport>(*BT, os.str(), N);
280 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
282 Report->markInteresting(AS->Region);
289 idx = getTrackedFunctionIndex(funName,
false);
290 if (idx == InvalidIdx)
293 unsigned paramIdx = FunctionsToTrack[idx].Param;
309 bool RegionArgIsBad =
false;
313 RegionArgIsBad =
true;
317 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
323 if (RegionArgIsBad) {
326 if (isEnclosingFunctionParam(ArgExpr))
333 auto Report = llvm::make_unique<BugReport>(
334 *BT,
"Trying to free data which has not been allocated.", N);
337 Report->markInteresting(AS->Region);
343 if (FunctionsToTrack[idx].
Kind == PossibleAPI) {
345 if (funName ==
"CFStringCreateWithBytesNoCopy") {
350 const AllocationPair AP = std::make_pair(ArgSM, AS);
351 generateDeallocatorMismatchReport(AP, ArgExpr, C);
355 if (
const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
356 StringRef DeallocatorName = DE->getFoundDecl()->getName();
357 if (DeallocatorName ==
"kCFAllocatorDefault" ||
358 DeallocatorName ==
"kCFAllocatorSystemDefault" ||
359 DeallocatorName ==
"kCFAllocatorMalloc") {
360 const AllocationPair AP = std::make_pair(ArgSM, AS);
361 generateDeallocatorMismatchReport(AP, ArgExpr, C);
366 if (DE->getFoundDecl()->getName() ==
"kCFAllocatorNull")
371 State = State->remove<AllocatedData>(ArgSM);
376 llvm_unreachable(
"We know of no other possible APIs.");
381 State = State->remove<AllocatedData>(ArgSM);
384 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
385 if (PDeallocIdx != idx || (FunctionsToTrack[idx].
Kind == ErrorAPI)) {
386 const AllocationPair AP = std::make_pair(ArgSM, AS);
387 generateDeallocatorMismatchReport(AP, ArgExpr, C);
394 void MacOSKeychainAPIChecker::checkPostStmt(
const CallExpr *CE,
404 unsigned idx = getTrackedFunctionIndex(funName,
true);
405 if (idx == InvalidIdx)
408 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[idx].Param);
411 if (isEnclosingFunctionParam(ArgExpr) &&
432 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
441 MacOSKeychainAPIChecker::getAllocationNode(
const ExplodedNode *N,
450 if (!N->
getState()->get<AllocatedData>(Sym))
455 if (NContext == LeakContext ||
464 std::unique_ptr<BugReport>
465 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
467 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
470 llvm::raw_svector_ostream os(sbuf);
471 os <<
"Allocated data is not released: missing a call to '" 472 << FunctionsToTrack[FI.DeallocatorIdx].Name <<
"'.";
478 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
487 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
490 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
491 markInteresting(Report.get(), AP);
499 bool Assumption)
const {
500 AllocatedDataTy AMap = State->get<AllocatedData>();
504 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymExpr());
508 if (OpCode != BO_EQ && OpCode != BO_NE)
514 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
515 const llvm::APInt &RHS = SIE->getRHS();
516 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
517 (OpCode == BO_NE && RHS == NoErr);
519 ErrorIsReturned = !ErrorIsReturned;
521 ReturnSymbol = SIE->getLHS();
525 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
526 if (ReturnSymbol == I->second.Region)
527 State = State->remove<AllocatedData>(I->first);
533 void MacOSKeychainAPIChecker::checkDeadSymbols(
SymbolReaper &SR,
536 AllocatedDataTy AMap = State->get<AllocatedData>();
540 bool Changed =
false;
541 AllocationPairVec Errors;
542 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
547 State = State->remove<AllocatedData>(I->first);
553 Errors.push_back(std::make_pair(I->first, &I->second));
567 for (
const auto &
P : Errors)
568 C.
emitReport(generateAllocatedDataNotReleasedReport(
P, N, C));
574 std::shared_ptr<PathDiagnosticPiece>
575 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
578 const AllocationState *AS = N->
getState()->get<AllocatedData>(Sym);
581 const AllocationState *ASPrev = PrevN->
getState()->get<AllocatedData>(Sym);
590 assert(funDecl &&
"We do not support indirect function calls as of now.");
591 StringRef funName = funDecl->getName();
594 unsigned Idx = getTrackedFunctionIndex(funName,
true);
595 assert(Idx != InvalidIdx &&
"This should be a call to an allocator.");
596 const Expr *ArgExpr = CE->
getArg(FunctionsToTrack[Idx].Param);
599 return std::make_shared<PathDiagnosticEventPiece>(Pos,
600 "Data is allocated here.");
603 void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
606 const char *Sep)
const {
608 AllocatedDataTy AMap = State->get<AllocatedData>();
610 if (!AMap.isEmpty()) {
611 Out << Sep <<
"KeychainAPIChecker :" << NL;
612 for (
auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
613 I.getKey()->dumpToStream(Out);
Represents 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).
constexpr XRayInstrMask Function
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.
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.
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.
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.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
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...
CHECKER * registerChecker(AT... Args)
Used to register checkers.
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)
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()