clang 19.0.0git
MacOSKeychainAPIChecker.cpp
Go to the documentation of this file.
1//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This checker flags misuses of KeyChainAPI. In particular, the password data
9// allocated/returned by SecKeychainItemCopyContent,
10// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
11// to be freed using a call to SecKeychainItemFreeContent.
12//===----------------------------------------------------------------------===//
13
22#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/Support/raw_ostream.h"
25#include <optional>
26
27using namespace clang;
28using namespace ento;
29
30namespace {
31class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
32 check::PostStmt<CallExpr>,
33 check::DeadSymbols,
34 check::PointerEscape,
35 eval::Assume> {
36 const BugType BT{this, "Improper use of SecKeychain API",
38
39public:
40 /// AllocationState is a part of the checker specific state together with the
41 /// MemRegion corresponding to the allocated data.
42 struct AllocationState {
43 /// The index of the allocator function.
44 unsigned int AllocatorIdx;
45 SymbolRef Region;
46
47 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
48 AllocatorIdx(Idx),
49 Region(R) {}
50
51 bool operator==(const AllocationState &X) const {
52 return (AllocatorIdx == X.AllocatorIdx &&
53 Region == X.Region);
54 }
55
56 void Profile(llvm::FoldingSetNodeID &ID) const {
57 ID.AddInteger(AllocatorIdx);
58 ID.AddPointer(Region);
59 }
60 };
61
62 void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
63 void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
64 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
65 ProgramStateRef checkPointerEscape(ProgramStateRef State,
66 const InvalidatedSymbols &Escaped,
67 const CallEvent *Call,
68 PointerEscapeKind Kind) const;
69 ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
70 bool Assumption) const;
71 void printState(raw_ostream &Out, ProgramStateRef State,
72 const char *NL, const char *Sep) const override;
73
74private:
75 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
76 typedef SmallVector<AllocationPair, 2> AllocationPairVec;
77
78 enum APIKind {
79 /// Denotes functions tracked by this checker.
80 ValidAPI = 0,
81 /// The functions commonly/mistakenly used in place of the given API.
82 ErrorAPI = 1,
83 /// The functions which may allocate the data. These are tracked to reduce
84 /// the false alarm rate.
85 PossibleAPI = 2
86 };
87 /// Stores the information about the allocator and deallocator functions -
88 /// these are the functions the checker is tracking.
89 struct ADFunctionInfo {
90 const char* Name;
91 unsigned int Param;
92 unsigned int DeallocatorIdx;
93 APIKind Kind;
94 };
95 static const unsigned InvalidIdx = 100000;
96 static const unsigned FunctionsToTrackSize = 8;
97 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
98 /// The value, which represents no error return value for allocator functions.
99 static const unsigned NoErr = 0;
100
101 /// Given the function name, returns the index of the allocator/deallocator
102 /// function.
103 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
104
105 void generateDeallocatorMismatchReport(const AllocationPair &AP,
106 const Expr *ArgExpr,
107 CheckerContext &C) const;
108
109 /// Find the allocation site for Sym on the path leading to the node N.
110 const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym,
111 CheckerContext &C) const;
112
113 std::unique_ptr<PathSensitiveBugReport>
114 generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
115 ExplodedNode *N,
116 CheckerContext &C) const;
117
118 /// Mark an AllocationPair interesting for diagnostic reporting.
119 void markInteresting(PathSensitiveBugReport *R,
120 const AllocationPair &AP) const {
121 R->markInteresting(AP.first);
122 R->markInteresting(AP.second->Region);
123 }
124
125 /// The bug visitor which allows us to print extra diagnostics along the
126 /// BugReport path. For example, showing the allocation site of the leaked
127 /// region.
128 class SecKeychainBugVisitor : public BugReporterVisitor {
129 protected:
130 // The allocated region symbol tracked by the main analysis.
131 SymbolRef Sym;
132
133 public:
134 SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
135
136 void Profile(llvm::FoldingSetNodeID &ID) const override {
137 static int X = 0;
138 ID.AddPointer(&X);
139 ID.AddPointer(Sym);
140 }
141
142 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
144 PathSensitiveBugReport &BR) override;
145 };
146};
147}
148
149/// ProgramState traits to store the currently allocated (and not yet freed)
150/// symbols. This is a map from the allocated content symbol to the
151/// corresponding AllocationState.
153 SymbolRef,
154 MacOSKeychainAPIChecker::AllocationState)
155
156static bool isEnclosingFunctionParam(const Expr *E) {
157 E = E->IgnoreParenCasts();
158 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
159 const ValueDecl *VD = DRE->getDecl();
160 if (isa<ImplicitParamDecl, ParmVarDecl>(VD))
161 return true;
162 }
163 return false;
164}
165
166const MacOSKeychainAPIChecker::ADFunctionInfo
167 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
168 {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0
169 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1
170 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2
171 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3
172 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4
173 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5
174 {"free", 0, InvalidIdx, ErrorAPI}, // 6
175 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7
176};
177
178unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
179 bool IsAllocator) {
180 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
181 ADFunctionInfo FI = FunctionsToTrack[I];
182 if (FI.Name != Name)
183 continue;
184 // Make sure the function is of the right type (allocator vs deallocator).
185 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
186 return InvalidIdx;
187 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
188 return InvalidIdx;
189
190 return I;
191 }
192 // The function is not tracked.
193 return InvalidIdx;
194}
195
196static bool isBadDeallocationArgument(const MemRegion *Arg) {
197 if (!Arg)
198 return false;
199 return isa<AllocaRegion, BlockDataRegion, TypedRegion>(Arg);
200}
201
202/// Given the address expression, retrieve the value it's pointing to. Assume
203/// that value is itself an address, and return the corresponding symbol.
205 CheckerContext &C) {
206 ProgramStateRef State = C.getState();
207 SVal ArgV = C.getSVal(Expr);
208
209 if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
210 StoreManager& SM = C.getStoreManager();
211 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
212 if (sym)
213 return sym;
214 }
215 return nullptr;
216}
217
218// Report deallocator mismatch. Remove the region from tracking - reporting a
219// missing free error after this one is redundant.
220void MacOSKeychainAPIChecker::
221 generateDeallocatorMismatchReport(const AllocationPair &AP,
222 const Expr *ArgExpr,
223 CheckerContext &C) const {
224 ProgramStateRef State = C.getState();
225 State = State->remove<AllocatedData>(AP.first);
226 ExplodedNode *N = C.generateNonFatalErrorNode(State);
227
228 if (!N)
229 return;
230 SmallString<80> sbuf;
231 llvm::raw_svector_ostream os(sbuf);
232 unsigned int PDeallocIdx =
233 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
234
235 os << "Deallocator doesn't match the allocator: '"
236 << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
237 auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
238 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
239 Report->addRange(ArgExpr->getSourceRange());
240 markInteresting(Report.get(), AP);
241 C.emitReport(std::move(Report));
242}
243
244void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
245 CheckerContext &C) const {
246 unsigned idx = InvalidIdx;
247 ProgramStateRef State = C.getState();
248
249 const FunctionDecl *FD = C.getCalleeDecl(CE);
250 if (!FD || FD->getKind() != Decl::Function)
251 return;
252
253 StringRef funName = C.getCalleeName(FD);
254 if (funName.empty())
255 return;
256
257 // If it is a call to an allocator function, it could be a double allocation.
258 idx = getTrackedFunctionIndex(funName, true);
259 if (idx != InvalidIdx) {
260 unsigned paramIdx = FunctionsToTrack[idx].Param;
261 if (CE->getNumArgs() <= paramIdx)
262 return;
263
264 const Expr *ArgExpr = CE->getArg(paramIdx);
265 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
266 if (const AllocationState *AS = State->get<AllocatedData>(V)) {
267 // Remove the value from the state. The new symbol will be added for
268 // tracking when the second allocator is processed in checkPostStmt().
269 State = State->remove<AllocatedData>(V);
270 ExplodedNode *N = C.generateNonFatalErrorNode(State);
271 if (!N)
272 return;
273 SmallString<128> sbuf;
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
279 << "'.";
280 auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
281 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V));
282 Report->addRange(ArgExpr->getSourceRange());
283 Report->markInteresting(AS->Region);
284 C.emitReport(std::move(Report));
285 }
286 return;
287 }
288
289 // Is it a call to one of deallocator functions?
290 idx = getTrackedFunctionIndex(funName, false);
291 if (idx == InvalidIdx)
292 return;
293
294 unsigned paramIdx = FunctionsToTrack[idx].Param;
295 if (CE->getNumArgs() <= paramIdx)
296 return;
297
298 // Check the argument to the deallocator.
299 const Expr *ArgExpr = CE->getArg(paramIdx);
300 SVal ArgSVal = C.getSVal(ArgExpr);
301
302 // Undef is reported by another checker.
303 if (ArgSVal.isUndef())
304 return;
305
306 SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
307
308 // If the argument is coming from the heap, globals, or unknown, do not
309 // report it.
310 bool RegionArgIsBad = false;
311 if (!ArgSM) {
313 return;
314 RegionArgIsBad = true;
315 }
316
317 // Is the argument to the call being tracked?
318 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
319 if (!AS)
320 return;
321
322 // TODO: We might want to report double free here.
323 // (that would involve tracking all the freed symbols in the checker state).
324 if (RegionArgIsBad) {
325 // It is possible that this is a false positive - the argument might
326 // have entered as an enclosing function parameter.
327 if (isEnclosingFunctionParam(ArgExpr))
328 return;
329
330 ExplodedNode *N = C.generateNonFatalErrorNode(State);
331 if (!N)
332 return;
333 auto Report = std::make_unique<PathSensitiveBugReport>(
334 BT, "Trying to free data which has not been allocated.", N);
335 Report->addRange(ArgExpr->getSourceRange());
336 if (AS)
337 Report->markInteresting(AS->Region);
338 C.emitReport(std::move(Report));
339 return;
340 }
341
342 // Process functions which might deallocate.
343 if (FunctionsToTrack[idx].Kind == PossibleAPI) {
344
345 if (funName == "CFStringCreateWithBytesNoCopy") {
346 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
347 // NULL ~ default deallocator, so warn.
348 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
350 const AllocationPair AP = std::make_pair(ArgSM, AS);
351 generateDeallocatorMismatchReport(AP, ArgExpr, C);
352 return;
353 }
354 // One of the default allocators, so warn.
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);
362 return;
363 }
364 // If kCFAllocatorNull, which does not deallocate, we still have to
365 // find the deallocator.
366 if (DE->getFoundDecl()->getName() == "kCFAllocatorNull")
367 return;
368 }
369 // In all other cases, assume the user supplied a correct deallocator
370 // that will free memory so stop tracking.
371 State = State->remove<AllocatedData>(ArgSM);
372 C.addTransition(State);
373 return;
374 }
375
376 llvm_unreachable("We know of no other possible APIs.");
377 }
378
379 // The call is deallocating a value we previously allocated, so remove it
380 // from the next state.
381 State = State->remove<AllocatedData>(ArgSM);
382
383 // Check if the proper deallocator is used.
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);
388 return;
389 }
390
391 C.addTransition(State);
392}
393
394void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
395 CheckerContext &C) const {
396 ProgramStateRef State = C.getState();
397 const FunctionDecl *FD = C.getCalleeDecl(CE);
398 if (!FD || FD->getKind() != Decl::Function)
399 return;
400
401 StringRef funName = C.getCalleeName(FD);
402
403 // If a value has been allocated, add it to the set for tracking.
404 unsigned idx = getTrackedFunctionIndex(funName, true);
405 if (idx == InvalidIdx)
406 return;
407
408 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
409 // If the argument entered as an enclosing function parameter, skip it to
410 // avoid false positives.
411 if (isEnclosingFunctionParam(ArgExpr) &&
412 C.getLocationContext()->getParent() == nullptr)
413 return;
414
415 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
416 // If the argument points to something that's not a symbolic region, it
417 // can be:
418 // - unknown (cannot reason about it)
419 // - undefined (already reported by other checker)
420 // - constant (null - should not be tracked,
421 // other constant will generate a compiler warning)
422 // - goto (should be reported by other checker)
423
424 // The call return value symbol should stay alive for as long as the
425 // allocated value symbol, since our diagnostics depend on the value
426 // returned by the call. Ex: Data should only be freed if noErr was
427 // returned during allocation.)
428 SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
429 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
430
431 // Track the allocated value in the checker state.
432 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
433 RetStatusSymbol));
434 assert(State);
435 C.addTransition(State);
436 }
437}
438
439// TODO: This logic is the same as in Malloc checker.
440const ExplodedNode *
441MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,
442 SymbolRef Sym,
443 CheckerContext &C) const {
444 const LocationContext *LeakContext = N->getLocationContext();
445 // Walk the ExplodedGraph backwards and find the first node that referred to
446 // the tracked symbol.
447 const ExplodedNode *AllocNode = N;
448
449 while (N) {
450 if (!N->getState()->get<AllocatedData>(Sym))
451 break;
452 // Allocation node, is the last node in the current or parent context in
453 // which the symbol was tracked.
454 const LocationContext *NContext = N->getLocationContext();
455 if (NContext == LeakContext ||
456 NContext->isParentOf(LeakContext))
457 AllocNode = N;
458 N = N->pred_empty() ? nullptr : *(N->pred_begin());
459 }
460
461 return AllocNode;
462}
463
464std::unique_ptr<PathSensitiveBugReport>
465MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
466 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const {
467 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
468 SmallString<70> sbuf;
469 llvm::raw_svector_ostream os(sbuf);
470 os << "Allocated data is not released: missing a call to '"
471 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
472
473 // Most bug reports are cached at the location where they occurred.
474 // With leaks, we want to unique them by the location where they were
475 // allocated, and only report a single path.
476 PathDiagnosticLocation LocUsedForUniqueing;
477 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
478 const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics();
479
480 if (AllocStmt)
481 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
482 C.getSourceManager(),
483 AllocNode->getLocationContext());
484
485 auto Report = std::make_unique<PathSensitiveBugReport>(
486 BT, os.str(), N, LocUsedForUniqueing,
487 AllocNode->getLocationContext()->getDecl());
488
489 Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
490 markInteresting(Report.get(), AP);
491 return Report;
492}
493
494/// If the return symbol is assumed to be error, remove the allocated info
495/// from consideration.
496ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State,
497 SVal Cond,
498 bool Assumption) const {
499 AllocatedDataTy AMap = State->get<AllocatedData>();
500 if (AMap.isEmpty())
501 return State;
502
503 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
504 if (!CondBSE)
505 return State;
506 BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
507 if (OpCode != BO_EQ && OpCode != BO_NE)
508 return State;
509
510 // Match for a restricted set of patterns for cmparison of error codes.
511 // Note, the comparisons of type '0 == st' are transformed into SymIntExpr.
512 SymbolRef ReturnSymbol = nullptr;
513 if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
514 const llvm::APInt &RHS = SIE->getRHS();
515 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
516 (OpCode == BO_NE && RHS == NoErr);
517 if (!Assumption)
518 ErrorIsReturned = !ErrorIsReturned;
519 if (ErrorIsReturned)
520 ReturnSymbol = SIE->getLHS();
521 }
522
523 if (ReturnSymbol)
524 for (auto [Sym, AllocState] : AMap) {
525 if (ReturnSymbol == AllocState.Region)
526 State = State->remove<AllocatedData>(Sym);
527 }
528
529 return State;
530}
531
532void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
533 CheckerContext &C) const {
534 ProgramStateRef State = C.getState();
535 AllocatedDataTy AMap = State->get<AllocatedData>();
536 if (AMap.isEmpty())
537 return;
538
539 bool Changed = false;
540 AllocationPairVec Errors;
541 for (const auto &[Sym, AllocState] : AMap) {
542 if (!SR.isDead(Sym))
543 continue;
544
545 Changed = true;
546 State = State->remove<AllocatedData>(Sym);
547 // If the allocated symbol is null do not report.
548 ConstraintManager &CMgr = State->getConstraintManager();
549 ConditionTruthVal AllocFailed = CMgr.isNull(State, Sym);
550 if (AllocFailed.isConstrainedTrue())
551 continue;
552 Errors.push_back(std::make_pair(Sym, &AllocState));
553 }
554 if (!Changed) {
555 // Generate the new, cleaned up state.
556 C.addTransition(State);
557 return;
558 }
559
560 static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak");
561 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag);
562 if (!N)
563 return;
564
565 // Generate the error reports.
566 for (const auto &P : Errors)
567 C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C));
568
569 // Generate the new, cleaned up state.
570 C.addTransition(State, N);
571}
572
573ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape(
574 ProgramStateRef State, const InvalidatedSymbols &Escaped,
575 const CallEvent *Call, PointerEscapeKind Kind) const {
576 // FIXME: This branch doesn't make any sense at all, but it is an overfitted
577 // replacement for a previous overfitted code that was making even less sense.
578 if (!Call || Call->getDecl())
579 return State;
580
581 for (auto I : State->get<AllocatedData>()) {
582 SymbolRef Sym = I.first;
583 if (Escaped.count(Sym))
584 State = State->remove<AllocatedData>(Sym);
585
586 // This checker is special. Most checkers in fact only track symbols of
587 // SymbolConjured type, eg. symbols returned from functions such as
588 // malloc(). This checker tracks symbols returned as out-parameters.
589 //
590 // When a function is evaluated conservatively, the out-parameter's pointee
591 // base region gets invalidated with a SymbolConjured. If the base region is
592 // larger than the region we're interested in, the value we're interested in
593 // would be SymbolDerived based on that SymbolConjured. However, such
594 // SymbolDerived will never be listed in the Escaped set when the base
595 // region is invalidated because ExprEngine doesn't know which symbols
596 // were derived from a given symbol, while there can be infinitely many
597 // valid symbols derived from any given symbol.
598 //
599 // Hence the extra boilerplate: remove the derived symbol when its parent
600 // symbol escapes.
601 //
602 if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
603 SymbolRef ParentSym = SD->getParentSymbol();
604 if (Escaped.count(ParentSym))
605 State = State->remove<AllocatedData>(Sym);
606 }
607 }
608 return State;
609}
610
612MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
613 const ExplodedNode *N, BugReporterContext &BRC,
615 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
616 if (!AS)
617 return nullptr;
618 const AllocationState *ASPrev =
619 N->getFirstPred()->getState()->get<AllocatedData>(Sym);
620 if (ASPrev)
621 return nullptr;
622
623 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
624 // allocation site.
625 const CallExpr *CE =
626 cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt());
627 const FunctionDecl *funDecl = CE->getDirectCallee();
628 assert(funDecl && "We do not support indirect function calls as of now.");
629 StringRef funName = funDecl->getName();
630
631 // Get the expression of the corresponding argument.
632 unsigned Idx = getTrackedFunctionIndex(funName, true);
633 assert(Idx != InvalidIdx && "This should be a call to an allocator.");
634 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
635 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
636 N->getLocationContext());
637 return std::make_shared<PathDiagnosticEventPiece>(Pos,
638 "Data is allocated here.");
639}
640
641void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
642 ProgramStateRef State,
643 const char *NL,
644 const char *Sep) const {
645
646 AllocatedDataTy AMap = State->get<AllocatedData>();
647
648 if (!AMap.isEmpty()) {
649 Out << Sep << "KeychainAPIChecker :" << NL;
650 for (SymbolRef Sym : llvm::make_first_range(AMap)) {
651 Sym->dumpToStream(Out);
652 }
653 }
654}
655
656
657void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
658 mgr.registerChecker<MacOSKeychainAPIChecker>();
659}
660
661bool ento::shouldRegisterMacOSKeychainAPIChecker(const CheckerManager &mgr) {
662 return true;
663}
#define V(N, I)
Definition: ASTContext.h:3332
StringRef P
#define SM(sm)
Definition: Cuda.cpp:83
Expr * E
#define X(type, name)
Definition: Value.h:143
static bool isBadDeallocationArgument(const MemRegion *Arg)
static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)
Given the address expression, retrieve the value it's pointing to.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2830
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3021
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:3000
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:3008
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1265
Kind getKind() const
Definition: DeclBase.h:448
This represents one expression.
Definition: Expr.h:110
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:3075
@ NPC_ValueDependentIsNotNull
Specifies that a value-dependent expression should be considered to never be a null pointer constant.
Definition: Expr.h:830
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant.
Definition: Expr.cpp:3941
Represents a function declaration or definition.
Definition: Decl.h:1932
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
bool isParentOf(const LocationContext *LC) const
const Decl * getDecl() const
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:276
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type.
Definition: ProgramPoint.h:137
const Stmt * getStmt() const
Definition: ProgramPoint.h:274
Stmt - This represents one statement.
Definition: Stmt.h:84
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:326
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:667
const SourceManager & getSourceManager() const
Definition: BugReporter.h:737
BugReporterVisitors are used to add custom diagnostics along a path.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
Definition: Checker.h:496
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
Definition: Checker.h:505
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
const ProgramStateRef & getState() const
pred_iterator pred_begin()
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:97
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
bool isUndef() const
Definition: SVals.h:104
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:104
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
Definition: SVals.cpp:68
const MemRegion * getAsRegion() const
Definition: SVals.cpp:120
Symbolic value.
Definition: SymExpr.h:30
virtual void dumpToStream(raw_ostream &os) const
Definition: SymExpr.h:61
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
const char *const AppleAPIMisuse
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.
BinaryOperatorKind
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition: CallGraph.h:207