1f4a2713aSLionel Sambuc //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc // This checker flags misuses of KeyChainAPI. In particular, the password data
10f4a2713aSLionel Sambuc // allocated/returned by SecKeychainItemCopyContent,
11f4a2713aSLionel Sambuc // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
12f4a2713aSLionel Sambuc // to be freed using a call to SecKeychainItemFreeContent.
13f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
14f4a2713aSLionel Sambuc
15f4a2713aSLionel Sambuc #include "ClangSACheckers.h"
16f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/Checker.h"
18f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
21f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
22f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
23f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h"
24f4a2713aSLionel Sambuc
25f4a2713aSLionel Sambuc using namespace clang;
26f4a2713aSLionel Sambuc using namespace ento;
27f4a2713aSLionel Sambuc
28f4a2713aSLionel Sambuc namespace {
29f4a2713aSLionel Sambuc class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
30f4a2713aSLionel Sambuc check::PostStmt<CallExpr>,
31f4a2713aSLionel Sambuc check::DeadSymbols> {
32*0a6a1f1dSLionel Sambuc mutable std::unique_ptr<BugType> BT;
33f4a2713aSLionel Sambuc
34f4a2713aSLionel Sambuc public:
35f4a2713aSLionel Sambuc /// AllocationState is a part of the checker specific state together with the
36f4a2713aSLionel Sambuc /// MemRegion corresponding to the allocated data.
37f4a2713aSLionel Sambuc struct AllocationState {
38f4a2713aSLionel Sambuc /// The index of the allocator function.
39f4a2713aSLionel Sambuc unsigned int AllocatorIdx;
40f4a2713aSLionel Sambuc SymbolRef Region;
41f4a2713aSLionel Sambuc
AllocationState__anon7eca354f0111::MacOSKeychainAPIChecker::AllocationState42f4a2713aSLionel Sambuc AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
43f4a2713aSLionel Sambuc AllocatorIdx(Idx),
44f4a2713aSLionel Sambuc Region(R) {}
45f4a2713aSLionel Sambuc
operator ==__anon7eca354f0111::MacOSKeychainAPIChecker::AllocationState46f4a2713aSLionel Sambuc bool operator==(const AllocationState &X) const {
47f4a2713aSLionel Sambuc return (AllocatorIdx == X.AllocatorIdx &&
48f4a2713aSLionel Sambuc Region == X.Region);
49f4a2713aSLionel Sambuc }
50f4a2713aSLionel Sambuc
Profile__anon7eca354f0111::MacOSKeychainAPIChecker::AllocationState51f4a2713aSLionel Sambuc void Profile(llvm::FoldingSetNodeID &ID) const {
52f4a2713aSLionel Sambuc ID.AddInteger(AllocatorIdx);
53f4a2713aSLionel Sambuc ID.AddPointer(Region);
54f4a2713aSLionel Sambuc }
55f4a2713aSLionel Sambuc };
56f4a2713aSLionel Sambuc
57f4a2713aSLionel Sambuc void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
58f4a2713aSLionel Sambuc void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
59f4a2713aSLionel Sambuc void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
60f4a2713aSLionel Sambuc
61f4a2713aSLionel Sambuc private:
62f4a2713aSLionel Sambuc typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
63f4a2713aSLionel Sambuc typedef SmallVector<AllocationPair, 2> AllocationPairVec;
64f4a2713aSLionel Sambuc
65f4a2713aSLionel Sambuc enum APIKind {
66f4a2713aSLionel Sambuc /// Denotes functions tracked by this checker.
67f4a2713aSLionel Sambuc ValidAPI = 0,
68f4a2713aSLionel Sambuc /// The functions commonly/mistakenly used in place of the given API.
69f4a2713aSLionel Sambuc ErrorAPI = 1,
70f4a2713aSLionel Sambuc /// The functions which may allocate the data. These are tracked to reduce
71f4a2713aSLionel Sambuc /// the false alarm rate.
72f4a2713aSLionel Sambuc PossibleAPI = 2
73f4a2713aSLionel Sambuc };
74f4a2713aSLionel Sambuc /// Stores the information about the allocator and deallocator functions -
75f4a2713aSLionel Sambuc /// these are the functions the checker is tracking.
76f4a2713aSLionel Sambuc struct ADFunctionInfo {
77f4a2713aSLionel Sambuc const char* Name;
78f4a2713aSLionel Sambuc unsigned int Param;
79f4a2713aSLionel Sambuc unsigned int DeallocatorIdx;
80f4a2713aSLionel Sambuc APIKind Kind;
81f4a2713aSLionel Sambuc };
82f4a2713aSLionel Sambuc static const unsigned InvalidIdx = 100000;
83f4a2713aSLionel Sambuc static const unsigned FunctionsToTrackSize = 8;
84f4a2713aSLionel Sambuc static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
85f4a2713aSLionel Sambuc /// The value, which represents no error return value for allocator functions.
86f4a2713aSLionel Sambuc static const unsigned NoErr = 0;
87f4a2713aSLionel Sambuc
88f4a2713aSLionel Sambuc /// Given the function name, returns the index of the allocator/deallocator
89f4a2713aSLionel Sambuc /// function.
90f4a2713aSLionel Sambuc static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
91f4a2713aSLionel Sambuc
initBugType() const92f4a2713aSLionel Sambuc inline void initBugType() const {
93f4a2713aSLionel Sambuc if (!BT)
94*0a6a1f1dSLionel Sambuc BT.reset(new BugType(this, "Improper use of SecKeychain API",
95f4a2713aSLionel Sambuc "API Misuse (Apple)"));
96f4a2713aSLionel Sambuc }
97f4a2713aSLionel Sambuc
98f4a2713aSLionel Sambuc void generateDeallocatorMismatchReport(const AllocationPair &AP,
99f4a2713aSLionel Sambuc const Expr *ArgExpr,
100f4a2713aSLionel Sambuc CheckerContext &C) const;
101f4a2713aSLionel Sambuc
102f4a2713aSLionel Sambuc /// Find the allocation site for Sym on the path leading to the node N.
103f4a2713aSLionel Sambuc const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym,
104f4a2713aSLionel Sambuc CheckerContext &C) const;
105f4a2713aSLionel Sambuc
106f4a2713aSLionel Sambuc BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
107f4a2713aSLionel Sambuc ExplodedNode *N,
108f4a2713aSLionel Sambuc CheckerContext &C) const;
109f4a2713aSLionel Sambuc
110f4a2713aSLionel Sambuc /// Check if RetSym evaluates to an error value in the current state.
111f4a2713aSLionel Sambuc bool definitelyReturnedError(SymbolRef RetSym,
112f4a2713aSLionel Sambuc ProgramStateRef State,
113f4a2713aSLionel Sambuc SValBuilder &Builder,
114f4a2713aSLionel Sambuc bool noError = false) const;
115f4a2713aSLionel Sambuc
116f4a2713aSLionel Sambuc /// Check if RetSym evaluates to a NoErr value in the current state.
definitelyDidnotReturnError(SymbolRef RetSym,ProgramStateRef State,SValBuilder & Builder) const117f4a2713aSLionel Sambuc bool definitelyDidnotReturnError(SymbolRef RetSym,
118f4a2713aSLionel Sambuc ProgramStateRef State,
119f4a2713aSLionel Sambuc SValBuilder &Builder) const {
120f4a2713aSLionel Sambuc return definitelyReturnedError(RetSym, State, Builder, true);
121f4a2713aSLionel Sambuc }
122f4a2713aSLionel Sambuc
123f4a2713aSLionel Sambuc /// Mark an AllocationPair interesting for diagnostic reporting.
markInteresting(BugReport * R,const AllocationPair & AP) const124f4a2713aSLionel Sambuc void markInteresting(BugReport *R, const AllocationPair &AP) const {
125f4a2713aSLionel Sambuc R->markInteresting(AP.first);
126f4a2713aSLionel Sambuc R->markInteresting(AP.second->Region);
127f4a2713aSLionel Sambuc }
128f4a2713aSLionel Sambuc
129f4a2713aSLionel Sambuc /// The bug visitor which allows us to print extra diagnostics along the
130f4a2713aSLionel Sambuc /// BugReport path. For example, showing the allocation site of the leaked
131f4a2713aSLionel Sambuc /// region.
132f4a2713aSLionel Sambuc class SecKeychainBugVisitor
133f4a2713aSLionel Sambuc : public BugReporterVisitorImpl<SecKeychainBugVisitor> {
134f4a2713aSLionel Sambuc protected:
135f4a2713aSLionel Sambuc // The allocated region symbol tracked by the main analysis.
136f4a2713aSLionel Sambuc SymbolRef Sym;
137f4a2713aSLionel Sambuc
138f4a2713aSLionel Sambuc public:
SecKeychainBugVisitor(SymbolRef S)139f4a2713aSLionel Sambuc SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
~SecKeychainBugVisitor()140f4a2713aSLionel Sambuc virtual ~SecKeychainBugVisitor() {}
141f4a2713aSLionel Sambuc
Profile(llvm::FoldingSetNodeID & ID) const142*0a6a1f1dSLionel Sambuc void Profile(llvm::FoldingSetNodeID &ID) const override {
143f4a2713aSLionel Sambuc static int X = 0;
144f4a2713aSLionel Sambuc ID.AddPointer(&X);
145f4a2713aSLionel Sambuc ID.AddPointer(Sym);
146f4a2713aSLionel Sambuc }
147f4a2713aSLionel Sambuc
148f4a2713aSLionel Sambuc PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
149f4a2713aSLionel Sambuc const ExplodedNode *PrevN,
150f4a2713aSLionel Sambuc BugReporterContext &BRC,
151*0a6a1f1dSLionel Sambuc BugReport &BR) override;
152f4a2713aSLionel Sambuc };
153f4a2713aSLionel Sambuc };
154f4a2713aSLionel Sambuc }
155f4a2713aSLionel Sambuc
156f4a2713aSLionel Sambuc /// ProgramState traits to store the currently allocated (and not yet freed)
157f4a2713aSLionel Sambuc /// symbols. This is a map from the allocated content symbol to the
158f4a2713aSLionel Sambuc /// corresponding AllocationState.
REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData,SymbolRef,MacOSKeychainAPIChecker::AllocationState)159f4a2713aSLionel Sambuc REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData,
160f4a2713aSLionel Sambuc SymbolRef,
161f4a2713aSLionel Sambuc MacOSKeychainAPIChecker::AllocationState)
162f4a2713aSLionel Sambuc
163f4a2713aSLionel Sambuc static bool isEnclosingFunctionParam(const Expr *E) {
164f4a2713aSLionel Sambuc E = E->IgnoreParenCasts();
165f4a2713aSLionel Sambuc if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
166f4a2713aSLionel Sambuc const ValueDecl *VD = DRE->getDecl();
167f4a2713aSLionel Sambuc if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
168f4a2713aSLionel Sambuc return true;
169f4a2713aSLionel Sambuc }
170f4a2713aSLionel Sambuc return false;
171f4a2713aSLionel Sambuc }
172f4a2713aSLionel Sambuc
173f4a2713aSLionel Sambuc const MacOSKeychainAPIChecker::ADFunctionInfo
174f4a2713aSLionel Sambuc MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
175f4a2713aSLionel Sambuc {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0
176f4a2713aSLionel Sambuc {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1
177f4a2713aSLionel Sambuc {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2
178f4a2713aSLionel Sambuc {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3
179f4a2713aSLionel Sambuc {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4
180f4a2713aSLionel Sambuc {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5
181f4a2713aSLionel Sambuc {"free", 0, InvalidIdx, ErrorAPI}, // 6
182f4a2713aSLionel Sambuc {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7
183f4a2713aSLionel Sambuc };
184f4a2713aSLionel Sambuc
getTrackedFunctionIndex(StringRef Name,bool IsAllocator)185f4a2713aSLionel Sambuc unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
186f4a2713aSLionel Sambuc bool IsAllocator) {
187f4a2713aSLionel Sambuc for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
188f4a2713aSLionel Sambuc ADFunctionInfo FI = FunctionsToTrack[I];
189f4a2713aSLionel Sambuc if (FI.Name != Name)
190f4a2713aSLionel Sambuc continue;
191f4a2713aSLionel Sambuc // Make sure the function is of the right type (allocator vs deallocator).
192f4a2713aSLionel Sambuc if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
193f4a2713aSLionel Sambuc return InvalidIdx;
194f4a2713aSLionel Sambuc if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
195f4a2713aSLionel Sambuc return InvalidIdx;
196f4a2713aSLionel Sambuc
197f4a2713aSLionel Sambuc return I;
198f4a2713aSLionel Sambuc }
199f4a2713aSLionel Sambuc // The function is not tracked.
200f4a2713aSLionel Sambuc return InvalidIdx;
201f4a2713aSLionel Sambuc }
202f4a2713aSLionel Sambuc
isBadDeallocationArgument(const MemRegion * Arg)203f4a2713aSLionel Sambuc static bool isBadDeallocationArgument(const MemRegion *Arg) {
204f4a2713aSLionel Sambuc if (!Arg)
205f4a2713aSLionel Sambuc return false;
206f4a2713aSLionel Sambuc if (isa<AllocaRegion>(Arg) ||
207f4a2713aSLionel Sambuc isa<BlockDataRegion>(Arg) ||
208f4a2713aSLionel Sambuc isa<TypedRegion>(Arg)) {
209f4a2713aSLionel Sambuc return true;
210f4a2713aSLionel Sambuc }
211f4a2713aSLionel Sambuc return false;
212f4a2713aSLionel Sambuc }
213f4a2713aSLionel Sambuc
214f4a2713aSLionel Sambuc /// Given the address expression, retrieve the value it's pointing to. Assume
215f4a2713aSLionel Sambuc /// that value is itself an address, and return the corresponding symbol.
getAsPointeeSymbol(const Expr * Expr,CheckerContext & C)216f4a2713aSLionel Sambuc static SymbolRef getAsPointeeSymbol(const Expr *Expr,
217f4a2713aSLionel Sambuc CheckerContext &C) {
218f4a2713aSLionel Sambuc ProgramStateRef State = C.getState();
219f4a2713aSLionel Sambuc SVal ArgV = State->getSVal(Expr, C.getLocationContext());
220f4a2713aSLionel Sambuc
221f4a2713aSLionel Sambuc if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
222f4a2713aSLionel Sambuc StoreManager& SM = C.getStoreManager();
223f4a2713aSLionel Sambuc SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
224f4a2713aSLionel Sambuc if (sym)
225f4a2713aSLionel Sambuc return sym;
226f4a2713aSLionel Sambuc }
227*0a6a1f1dSLionel Sambuc return nullptr;
228f4a2713aSLionel Sambuc }
229f4a2713aSLionel Sambuc
230f4a2713aSLionel Sambuc // When checking for error code, we need to consider the following cases:
231f4a2713aSLionel Sambuc // 1) noErr / [0]
232f4a2713aSLionel Sambuc // 2) someErr / [1, inf]
233f4a2713aSLionel Sambuc // 3) unknown
234f4a2713aSLionel Sambuc // If noError, returns true iff (1).
235f4a2713aSLionel Sambuc // If !noError, returns true iff (2).
definitelyReturnedError(SymbolRef RetSym,ProgramStateRef State,SValBuilder & Builder,bool noError) const236f4a2713aSLionel Sambuc bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
237f4a2713aSLionel Sambuc ProgramStateRef State,
238f4a2713aSLionel Sambuc SValBuilder &Builder,
239f4a2713aSLionel Sambuc bool noError) const {
240f4a2713aSLionel Sambuc DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
241f4a2713aSLionel Sambuc Builder.getSymbolManager().getType(RetSym));
242f4a2713aSLionel Sambuc DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
243f4a2713aSLionel Sambuc nonloc::SymbolVal(RetSym));
244f4a2713aSLionel Sambuc ProgramStateRef ErrState = State->assume(NoErr, noError);
245f4a2713aSLionel Sambuc if (ErrState == State) {
246f4a2713aSLionel Sambuc return true;
247f4a2713aSLionel Sambuc }
248f4a2713aSLionel Sambuc
249f4a2713aSLionel Sambuc return false;
250f4a2713aSLionel Sambuc }
251f4a2713aSLionel Sambuc
252f4a2713aSLionel Sambuc // Report deallocator mismatch. Remove the region from tracking - reporting a
253f4a2713aSLionel Sambuc // missing free error after this one is redundant.
254f4a2713aSLionel Sambuc void MacOSKeychainAPIChecker::
generateDeallocatorMismatchReport(const AllocationPair & AP,const Expr * ArgExpr,CheckerContext & C) const255f4a2713aSLionel Sambuc generateDeallocatorMismatchReport(const AllocationPair &AP,
256f4a2713aSLionel Sambuc const Expr *ArgExpr,
257f4a2713aSLionel Sambuc CheckerContext &C) const {
258f4a2713aSLionel Sambuc ProgramStateRef State = C.getState();
259f4a2713aSLionel Sambuc State = State->remove<AllocatedData>(AP.first);
260f4a2713aSLionel Sambuc ExplodedNode *N = C.addTransition(State);
261f4a2713aSLionel Sambuc
262f4a2713aSLionel Sambuc if (!N)
263f4a2713aSLionel Sambuc return;
264f4a2713aSLionel Sambuc initBugType();
265f4a2713aSLionel Sambuc SmallString<80> sbuf;
266f4a2713aSLionel Sambuc llvm::raw_svector_ostream os(sbuf);
267f4a2713aSLionel Sambuc unsigned int PDeallocIdx =
268f4a2713aSLionel Sambuc FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
269f4a2713aSLionel Sambuc
270f4a2713aSLionel Sambuc os << "Deallocator doesn't match the allocator: '"
271f4a2713aSLionel Sambuc << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
272f4a2713aSLionel Sambuc BugReport *Report = new BugReport(*BT, os.str(), N);
273*0a6a1f1dSLionel Sambuc Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
274f4a2713aSLionel Sambuc Report->addRange(ArgExpr->getSourceRange());
275f4a2713aSLionel Sambuc markInteresting(Report, AP);
276f4a2713aSLionel Sambuc C.emitReport(Report);
277f4a2713aSLionel Sambuc }
278f4a2713aSLionel Sambuc
checkPreStmt(const CallExpr * CE,CheckerContext & C) const279f4a2713aSLionel Sambuc void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
280f4a2713aSLionel Sambuc CheckerContext &C) const {
281f4a2713aSLionel Sambuc unsigned idx = InvalidIdx;
282f4a2713aSLionel Sambuc ProgramStateRef State = C.getState();
283f4a2713aSLionel Sambuc
284f4a2713aSLionel Sambuc const FunctionDecl *FD = C.getCalleeDecl(CE);
285f4a2713aSLionel Sambuc if (!FD || FD->getKind() != Decl::Function)
286f4a2713aSLionel Sambuc return;
287f4a2713aSLionel Sambuc
288f4a2713aSLionel Sambuc StringRef funName = C.getCalleeName(FD);
289f4a2713aSLionel Sambuc if (funName.empty())
290f4a2713aSLionel Sambuc return;
291f4a2713aSLionel Sambuc
292f4a2713aSLionel Sambuc // If it is a call to an allocator function, it could be a double allocation.
293f4a2713aSLionel Sambuc idx = getTrackedFunctionIndex(funName, true);
294f4a2713aSLionel Sambuc if (idx != InvalidIdx) {
295f4a2713aSLionel Sambuc const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
296f4a2713aSLionel Sambuc if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
297f4a2713aSLionel Sambuc if (const AllocationState *AS = State->get<AllocatedData>(V)) {
298f4a2713aSLionel Sambuc if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
299f4a2713aSLionel Sambuc // Remove the value from the state. The new symbol will be added for
300f4a2713aSLionel Sambuc // tracking when the second allocator is processed in checkPostStmt().
301f4a2713aSLionel Sambuc State = State->remove<AllocatedData>(V);
302f4a2713aSLionel Sambuc ExplodedNode *N = C.addTransition(State);
303f4a2713aSLionel Sambuc if (!N)
304f4a2713aSLionel Sambuc return;
305f4a2713aSLionel Sambuc initBugType();
306f4a2713aSLionel Sambuc SmallString<128> sbuf;
307f4a2713aSLionel Sambuc llvm::raw_svector_ostream os(sbuf);
308f4a2713aSLionel Sambuc unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
309f4a2713aSLionel Sambuc os << "Allocated data should be released before another call to "
310f4a2713aSLionel Sambuc << "the allocator: missing a call to '"
311f4a2713aSLionel Sambuc << FunctionsToTrack[DIdx].Name
312f4a2713aSLionel Sambuc << "'.";
313f4a2713aSLionel Sambuc BugReport *Report = new BugReport(*BT, os.str(), N);
314*0a6a1f1dSLionel Sambuc Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
315f4a2713aSLionel Sambuc Report->addRange(ArgExpr->getSourceRange());
316f4a2713aSLionel Sambuc Report->markInteresting(AS->Region);
317f4a2713aSLionel Sambuc C.emitReport(Report);
318f4a2713aSLionel Sambuc }
319f4a2713aSLionel Sambuc }
320f4a2713aSLionel Sambuc return;
321f4a2713aSLionel Sambuc }
322f4a2713aSLionel Sambuc
323f4a2713aSLionel Sambuc // Is it a call to one of deallocator functions?
324f4a2713aSLionel Sambuc idx = getTrackedFunctionIndex(funName, false);
325f4a2713aSLionel Sambuc if (idx == InvalidIdx)
326f4a2713aSLionel Sambuc return;
327f4a2713aSLionel Sambuc
328f4a2713aSLionel Sambuc // Check the argument to the deallocator.
329f4a2713aSLionel Sambuc const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
330f4a2713aSLionel Sambuc SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());
331f4a2713aSLionel Sambuc
332f4a2713aSLionel Sambuc // Undef is reported by another checker.
333f4a2713aSLionel Sambuc if (ArgSVal.isUndef())
334f4a2713aSLionel Sambuc return;
335f4a2713aSLionel Sambuc
336f4a2713aSLionel Sambuc SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
337f4a2713aSLionel Sambuc
338f4a2713aSLionel Sambuc // If the argument is coming from the heap, globals, or unknown, do not
339f4a2713aSLionel Sambuc // report it.
340f4a2713aSLionel Sambuc bool RegionArgIsBad = false;
341f4a2713aSLionel Sambuc if (!ArgSM) {
342f4a2713aSLionel Sambuc if (!isBadDeallocationArgument(ArgSVal.getAsRegion()))
343f4a2713aSLionel Sambuc return;
344f4a2713aSLionel Sambuc RegionArgIsBad = true;
345f4a2713aSLionel Sambuc }
346f4a2713aSLionel Sambuc
347f4a2713aSLionel Sambuc // Is the argument to the call being tracked?
348f4a2713aSLionel Sambuc const AllocationState *AS = State->get<AllocatedData>(ArgSM);
349f4a2713aSLionel Sambuc if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
350f4a2713aSLionel Sambuc return;
351f4a2713aSLionel Sambuc }
352f4a2713aSLionel Sambuc // If trying to free data which has not been allocated yet, report as a bug.
353f4a2713aSLionel Sambuc // TODO: We might want a more precise diagnostic for double free
354f4a2713aSLionel Sambuc // (that would involve tracking all the freed symbols in the checker state).
355f4a2713aSLionel Sambuc if (!AS || RegionArgIsBad) {
356f4a2713aSLionel Sambuc // It is possible that this is a false positive - the argument might
357f4a2713aSLionel Sambuc // have entered as an enclosing function parameter.
358f4a2713aSLionel Sambuc if (isEnclosingFunctionParam(ArgExpr))
359f4a2713aSLionel Sambuc return;
360f4a2713aSLionel Sambuc
361f4a2713aSLionel Sambuc ExplodedNode *N = C.addTransition(State);
362f4a2713aSLionel Sambuc if (!N)
363f4a2713aSLionel Sambuc return;
364f4a2713aSLionel Sambuc initBugType();
365f4a2713aSLionel Sambuc BugReport *Report = new BugReport(*BT,
366f4a2713aSLionel Sambuc "Trying to free data which has not been allocated.", N);
367f4a2713aSLionel Sambuc Report->addRange(ArgExpr->getSourceRange());
368f4a2713aSLionel Sambuc if (AS)
369f4a2713aSLionel Sambuc Report->markInteresting(AS->Region);
370f4a2713aSLionel Sambuc C.emitReport(Report);
371f4a2713aSLionel Sambuc return;
372f4a2713aSLionel Sambuc }
373f4a2713aSLionel Sambuc
374f4a2713aSLionel Sambuc // Process functions which might deallocate.
375f4a2713aSLionel Sambuc if (FunctionsToTrack[idx].Kind == PossibleAPI) {
376f4a2713aSLionel Sambuc
377f4a2713aSLionel Sambuc if (funName == "CFStringCreateWithBytesNoCopy") {
378f4a2713aSLionel Sambuc const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
379f4a2713aSLionel Sambuc // NULL ~ default deallocator, so warn.
380f4a2713aSLionel Sambuc if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
381f4a2713aSLionel Sambuc Expr::NPC_ValueDependentIsNotNull)) {
382f4a2713aSLionel Sambuc const AllocationPair AP = std::make_pair(ArgSM, AS);
383f4a2713aSLionel Sambuc generateDeallocatorMismatchReport(AP, ArgExpr, C);
384f4a2713aSLionel Sambuc return;
385f4a2713aSLionel Sambuc }
386f4a2713aSLionel Sambuc // One of the default allocators, so warn.
387f4a2713aSLionel Sambuc if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
388f4a2713aSLionel Sambuc StringRef DeallocatorName = DE->getFoundDecl()->getName();
389f4a2713aSLionel Sambuc if (DeallocatorName == "kCFAllocatorDefault" ||
390f4a2713aSLionel Sambuc DeallocatorName == "kCFAllocatorSystemDefault" ||
391f4a2713aSLionel Sambuc DeallocatorName == "kCFAllocatorMalloc") {
392f4a2713aSLionel Sambuc const AllocationPair AP = std::make_pair(ArgSM, AS);
393f4a2713aSLionel Sambuc generateDeallocatorMismatchReport(AP, ArgExpr, C);
394f4a2713aSLionel Sambuc return;
395f4a2713aSLionel Sambuc }
396f4a2713aSLionel Sambuc // If kCFAllocatorNull, which does not deallocate, we still have to
397f4a2713aSLionel Sambuc // find the deallocator.
398f4a2713aSLionel Sambuc if (DE->getFoundDecl()->getName() == "kCFAllocatorNull")
399f4a2713aSLionel Sambuc return;
400f4a2713aSLionel Sambuc }
401f4a2713aSLionel Sambuc // In all other cases, assume the user supplied a correct deallocator
402f4a2713aSLionel Sambuc // that will free memory so stop tracking.
403f4a2713aSLionel Sambuc State = State->remove<AllocatedData>(ArgSM);
404f4a2713aSLionel Sambuc C.addTransition(State);
405f4a2713aSLionel Sambuc return;
406f4a2713aSLionel Sambuc }
407f4a2713aSLionel Sambuc
408f4a2713aSLionel Sambuc llvm_unreachable("We know of no other possible APIs.");
409f4a2713aSLionel Sambuc }
410f4a2713aSLionel Sambuc
411f4a2713aSLionel Sambuc // The call is deallocating a value we previously allocated, so remove it
412f4a2713aSLionel Sambuc // from the next state.
413f4a2713aSLionel Sambuc State = State->remove<AllocatedData>(ArgSM);
414f4a2713aSLionel Sambuc
415f4a2713aSLionel Sambuc // Check if the proper deallocator is used.
416f4a2713aSLionel Sambuc unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
417f4a2713aSLionel Sambuc if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
418f4a2713aSLionel Sambuc const AllocationPair AP = std::make_pair(ArgSM, AS);
419f4a2713aSLionel Sambuc generateDeallocatorMismatchReport(AP, ArgExpr, C);
420f4a2713aSLionel Sambuc return;
421f4a2713aSLionel Sambuc }
422f4a2713aSLionel Sambuc
423f4a2713aSLionel Sambuc // If the buffer can be null and the return status can be an error,
424f4a2713aSLionel Sambuc // report a bad call to free.
425f4a2713aSLionel Sambuc if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) &&
426f4a2713aSLionel Sambuc !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
427f4a2713aSLionel Sambuc ExplodedNode *N = C.addTransition(State);
428f4a2713aSLionel Sambuc if (!N)
429f4a2713aSLionel Sambuc return;
430f4a2713aSLionel Sambuc initBugType();
431f4a2713aSLionel Sambuc BugReport *Report = new BugReport(*BT,
432f4a2713aSLionel Sambuc "Only call free if a valid (non-NULL) buffer was returned.", N);
433*0a6a1f1dSLionel Sambuc Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM));
434f4a2713aSLionel Sambuc Report->addRange(ArgExpr->getSourceRange());
435f4a2713aSLionel Sambuc Report->markInteresting(AS->Region);
436f4a2713aSLionel Sambuc C.emitReport(Report);
437f4a2713aSLionel Sambuc return;
438f4a2713aSLionel Sambuc }
439f4a2713aSLionel Sambuc
440f4a2713aSLionel Sambuc C.addTransition(State);
441f4a2713aSLionel Sambuc }
442f4a2713aSLionel Sambuc
checkPostStmt(const CallExpr * CE,CheckerContext & C) const443f4a2713aSLionel Sambuc void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
444f4a2713aSLionel Sambuc CheckerContext &C) const {
445f4a2713aSLionel Sambuc ProgramStateRef State = C.getState();
446f4a2713aSLionel Sambuc const FunctionDecl *FD = C.getCalleeDecl(CE);
447f4a2713aSLionel Sambuc if (!FD || FD->getKind() != Decl::Function)
448f4a2713aSLionel Sambuc return;
449f4a2713aSLionel Sambuc
450f4a2713aSLionel Sambuc StringRef funName = C.getCalleeName(FD);
451f4a2713aSLionel Sambuc
452f4a2713aSLionel Sambuc // If a value has been allocated, add it to the set for tracking.
453f4a2713aSLionel Sambuc unsigned idx = getTrackedFunctionIndex(funName, true);
454f4a2713aSLionel Sambuc if (idx == InvalidIdx)
455f4a2713aSLionel Sambuc return;
456f4a2713aSLionel Sambuc
457f4a2713aSLionel Sambuc const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
458f4a2713aSLionel Sambuc // If the argument entered as an enclosing function parameter, skip it to
459f4a2713aSLionel Sambuc // avoid false positives.
460f4a2713aSLionel Sambuc if (isEnclosingFunctionParam(ArgExpr) &&
461*0a6a1f1dSLionel Sambuc C.getLocationContext()->getParent() == nullptr)
462f4a2713aSLionel Sambuc return;
463f4a2713aSLionel Sambuc
464f4a2713aSLionel Sambuc if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
465f4a2713aSLionel Sambuc // If the argument points to something that's not a symbolic region, it
466f4a2713aSLionel Sambuc // can be:
467f4a2713aSLionel Sambuc // - unknown (cannot reason about it)
468f4a2713aSLionel Sambuc // - undefined (already reported by other checker)
469f4a2713aSLionel Sambuc // - constant (null - should not be tracked,
470f4a2713aSLionel Sambuc // other constant will generate a compiler warning)
471f4a2713aSLionel Sambuc // - goto (should be reported by other checker)
472f4a2713aSLionel Sambuc
473f4a2713aSLionel Sambuc // The call return value symbol should stay alive for as long as the
474f4a2713aSLionel Sambuc // allocated value symbol, since our diagnostics depend on the value
475f4a2713aSLionel Sambuc // returned by the call. Ex: Data should only be freed if noErr was
476f4a2713aSLionel Sambuc // returned during allocation.)
477f4a2713aSLionel Sambuc SymbolRef RetStatusSymbol =
478f4a2713aSLionel Sambuc State->getSVal(CE, C.getLocationContext()).getAsSymbol();
479f4a2713aSLionel Sambuc C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
480f4a2713aSLionel Sambuc
481f4a2713aSLionel Sambuc // Track the allocated value in the checker state.
482f4a2713aSLionel Sambuc State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
483f4a2713aSLionel Sambuc RetStatusSymbol));
484f4a2713aSLionel Sambuc assert(State);
485f4a2713aSLionel Sambuc C.addTransition(State);
486f4a2713aSLionel Sambuc }
487f4a2713aSLionel Sambuc }
488f4a2713aSLionel Sambuc
489f4a2713aSLionel Sambuc // TODO: This logic is the same as in Malloc checker.
490f4a2713aSLionel Sambuc const ExplodedNode *
getAllocationNode(const ExplodedNode * N,SymbolRef Sym,CheckerContext & C) const491f4a2713aSLionel Sambuc MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,
492f4a2713aSLionel Sambuc SymbolRef Sym,
493f4a2713aSLionel Sambuc CheckerContext &C) const {
494f4a2713aSLionel Sambuc const LocationContext *LeakContext = N->getLocationContext();
495f4a2713aSLionel Sambuc // Walk the ExplodedGraph backwards and find the first node that referred to
496f4a2713aSLionel Sambuc // the tracked symbol.
497f4a2713aSLionel Sambuc const ExplodedNode *AllocNode = N;
498f4a2713aSLionel Sambuc
499f4a2713aSLionel Sambuc while (N) {
500f4a2713aSLionel Sambuc if (!N->getState()->get<AllocatedData>(Sym))
501f4a2713aSLionel Sambuc break;
502f4a2713aSLionel Sambuc // Allocation node, is the last node in the current context in which the
503f4a2713aSLionel Sambuc // symbol was tracked.
504f4a2713aSLionel Sambuc if (N->getLocationContext() == LeakContext)
505f4a2713aSLionel Sambuc AllocNode = N;
506*0a6a1f1dSLionel Sambuc N = N->pred_empty() ? nullptr : *(N->pred_begin());
507f4a2713aSLionel Sambuc }
508f4a2713aSLionel Sambuc
509f4a2713aSLionel Sambuc return AllocNode;
510f4a2713aSLionel Sambuc }
511f4a2713aSLionel Sambuc
512f4a2713aSLionel Sambuc BugReport *MacOSKeychainAPIChecker::
generateAllocatedDataNotReleasedReport(const AllocationPair & AP,ExplodedNode * N,CheckerContext & C) const513f4a2713aSLionel Sambuc generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
514f4a2713aSLionel Sambuc ExplodedNode *N,
515f4a2713aSLionel Sambuc CheckerContext &C) const {
516f4a2713aSLionel Sambuc const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
517f4a2713aSLionel Sambuc initBugType();
518f4a2713aSLionel Sambuc SmallString<70> sbuf;
519f4a2713aSLionel Sambuc llvm::raw_svector_ostream os(sbuf);
520f4a2713aSLionel Sambuc os << "Allocated data is not released: missing a call to '"
521f4a2713aSLionel Sambuc << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
522f4a2713aSLionel Sambuc
523f4a2713aSLionel Sambuc // Most bug reports are cached at the location where they occurred.
524f4a2713aSLionel Sambuc // With leaks, we want to unique them by the location where they were
525f4a2713aSLionel Sambuc // allocated, and only report a single path.
526f4a2713aSLionel Sambuc PathDiagnosticLocation LocUsedForUniqueing;
527f4a2713aSLionel Sambuc const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
528*0a6a1f1dSLionel Sambuc const Stmt *AllocStmt = nullptr;
529f4a2713aSLionel Sambuc ProgramPoint P = AllocNode->getLocation();
530f4a2713aSLionel Sambuc if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
531f4a2713aSLionel Sambuc AllocStmt = Exit->getCalleeContext()->getCallSite();
532f4a2713aSLionel Sambuc else if (Optional<clang::PostStmt> PS = P.getAs<clang::PostStmt>())
533f4a2713aSLionel Sambuc AllocStmt = PS->getStmt();
534f4a2713aSLionel Sambuc
535f4a2713aSLionel Sambuc if (AllocStmt)
536f4a2713aSLionel Sambuc LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
537f4a2713aSLionel Sambuc C.getSourceManager(),
538f4a2713aSLionel Sambuc AllocNode->getLocationContext());
539f4a2713aSLionel Sambuc
540f4a2713aSLionel Sambuc BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing,
541f4a2713aSLionel Sambuc AllocNode->getLocationContext()->getDecl());
542f4a2713aSLionel Sambuc
543*0a6a1f1dSLionel Sambuc Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
544f4a2713aSLionel Sambuc markInteresting(Report, AP);
545f4a2713aSLionel Sambuc return Report;
546f4a2713aSLionel Sambuc }
547f4a2713aSLionel Sambuc
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const548f4a2713aSLionel Sambuc void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
549f4a2713aSLionel Sambuc CheckerContext &C) const {
550f4a2713aSLionel Sambuc ProgramStateRef State = C.getState();
551f4a2713aSLionel Sambuc AllocatedDataTy ASet = State->get<AllocatedData>();
552f4a2713aSLionel Sambuc if (ASet.isEmpty())
553f4a2713aSLionel Sambuc return;
554f4a2713aSLionel Sambuc
555f4a2713aSLionel Sambuc bool Changed = false;
556f4a2713aSLionel Sambuc AllocationPairVec Errors;
557f4a2713aSLionel Sambuc for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
558f4a2713aSLionel Sambuc if (SR.isLive(I->first))
559f4a2713aSLionel Sambuc continue;
560f4a2713aSLionel Sambuc
561f4a2713aSLionel Sambuc Changed = true;
562f4a2713aSLionel Sambuc State = State->remove<AllocatedData>(I->first);
563f4a2713aSLionel Sambuc // If the allocated symbol is null or if the allocation call might have
564f4a2713aSLionel Sambuc // returned an error, do not report.
565f4a2713aSLionel Sambuc ConstraintManager &CMgr = State->getConstraintManager();
566f4a2713aSLionel Sambuc ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
567f4a2713aSLionel Sambuc if (AllocFailed.isConstrainedTrue() ||
568f4a2713aSLionel Sambuc definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
569f4a2713aSLionel Sambuc continue;
570f4a2713aSLionel Sambuc Errors.push_back(std::make_pair(I->first, &I->second));
571f4a2713aSLionel Sambuc }
572f4a2713aSLionel Sambuc if (!Changed) {
573f4a2713aSLionel Sambuc // Generate the new, cleaned up state.
574f4a2713aSLionel Sambuc C.addTransition(State);
575f4a2713aSLionel Sambuc return;
576f4a2713aSLionel Sambuc }
577f4a2713aSLionel Sambuc
578*0a6a1f1dSLionel Sambuc static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak");
579f4a2713aSLionel Sambuc ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
580f4a2713aSLionel Sambuc
581f4a2713aSLionel Sambuc // Generate the error reports.
582f4a2713aSLionel Sambuc for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
583f4a2713aSLionel Sambuc I != E; ++I) {
584f4a2713aSLionel Sambuc C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C));
585f4a2713aSLionel Sambuc }
586f4a2713aSLionel Sambuc
587f4a2713aSLionel Sambuc // Generate the new, cleaned up state.
588f4a2713aSLionel Sambuc C.addTransition(State, N);
589f4a2713aSLionel Sambuc }
590f4a2713aSLionel Sambuc
591f4a2713aSLionel Sambuc
VisitNode(const ExplodedNode * N,const ExplodedNode * PrevN,BugReporterContext & BRC,BugReport & BR)592f4a2713aSLionel Sambuc PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
593f4a2713aSLionel Sambuc const ExplodedNode *N,
594f4a2713aSLionel Sambuc const ExplodedNode *PrevN,
595f4a2713aSLionel Sambuc BugReporterContext &BRC,
596f4a2713aSLionel Sambuc BugReport &BR) {
597f4a2713aSLionel Sambuc const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
598f4a2713aSLionel Sambuc if (!AS)
599*0a6a1f1dSLionel Sambuc return nullptr;
600f4a2713aSLionel Sambuc const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
601f4a2713aSLionel Sambuc if (ASPrev)
602*0a6a1f1dSLionel Sambuc return nullptr;
603f4a2713aSLionel Sambuc
604f4a2713aSLionel Sambuc // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
605f4a2713aSLionel Sambuc // allocation site.
606f4a2713aSLionel Sambuc const CallExpr *CE =
607f4a2713aSLionel Sambuc cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt());
608f4a2713aSLionel Sambuc const FunctionDecl *funDecl = CE->getDirectCallee();
609f4a2713aSLionel Sambuc assert(funDecl && "We do not support indirect function calls as of now.");
610f4a2713aSLionel Sambuc StringRef funName = funDecl->getName();
611f4a2713aSLionel Sambuc
612f4a2713aSLionel Sambuc // Get the expression of the corresponding argument.
613f4a2713aSLionel Sambuc unsigned Idx = getTrackedFunctionIndex(funName, true);
614f4a2713aSLionel Sambuc assert(Idx != InvalidIdx && "This should be a call to an allocator.");
615f4a2713aSLionel Sambuc const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
616f4a2713aSLionel Sambuc PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
617f4a2713aSLionel Sambuc N->getLocationContext());
618f4a2713aSLionel Sambuc return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
619f4a2713aSLionel Sambuc }
620f4a2713aSLionel Sambuc
registerMacOSKeychainAPIChecker(CheckerManager & mgr)621f4a2713aSLionel Sambuc void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
622f4a2713aSLionel Sambuc mgr.registerChecker<MacOSKeychainAPIChecker>();
623f4a2713aSLionel Sambuc }
624