diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp')
-rw-r--r-- | contrib/libs/clang16/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp new file mode 100644 index 0000000000..c1b85ace3e --- /dev/null +++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -0,0 +1,672 @@ +//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This checker flags misuses of KeyChainAPI. In particular, the password data +// allocated/returned by SecKeychainItemCopyContent, +// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has +// to be freed using a call to SecKeychainItemFreeContent. +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +using namespace clang; +using namespace ento; + +namespace { +class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, + check::PostStmt<CallExpr>, + check::DeadSymbols, + check::PointerEscape, + eval::Assume> { + mutable std::unique_ptr<BugType> BT; + +public: + /// AllocationState is a part of the checker specific state together with the + /// MemRegion corresponding to the allocated data. + struct AllocationState { + /// The index of the allocator function. + unsigned int AllocatorIdx; + SymbolRef Region; + + AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : + AllocatorIdx(Idx), + Region(R) {} + + bool operator==(const AllocationState &X) const { + return (AllocatorIdx == X.AllocatorIdx && + Region == X.Region); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(AllocatorIdx); + ID.AddPointer(Region); + } + }; + + void checkPreStmt(const CallExpr *S, CheckerContext &C) const; + void checkPostStmt(const CallExpr *S, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, + bool Assumption) const; + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; + +private: + typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; + typedef SmallVector<AllocationPair, 2> AllocationPairVec; + + enum APIKind { + /// Denotes functions tracked by this checker. + ValidAPI = 0, + /// The functions commonly/mistakenly used in place of the given API. + ErrorAPI = 1, + /// The functions which may allocate the data. These are tracked to reduce + /// the false alarm rate. + PossibleAPI = 2 + }; + /// Stores the information about the allocator and deallocator functions - + /// these are the functions the checker is tracking. + struct ADFunctionInfo { + const char* Name; + unsigned int Param; + unsigned int DeallocatorIdx; + APIKind Kind; + }; + static const unsigned InvalidIdx = 100000; + static const unsigned FunctionsToTrackSize = 8; + static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; + /// The value, which represents no error return value for allocator functions. + static const unsigned NoErr = 0; + + /// Given the function name, returns the index of the allocator/deallocator + /// function. + static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); + + inline void initBugType() const { + if (!BT) + BT.reset(new BugType(this, "Improper use of SecKeychain API", + "API Misuse (Apple)")); + } + + void generateDeallocatorMismatchReport(const AllocationPair &AP, + const Expr *ArgExpr, + CheckerContext &C) const; + + /// Find the allocation site for Sym on the path leading to the node N. + const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const; + + std::unique_ptr<PathSensitiveBugReport> + generateAllocatedDataNotReleasedReport(const AllocationPair &AP, + ExplodedNode *N, + CheckerContext &C) const; + + /// Mark an AllocationPair interesting for diagnostic reporting. + void markInteresting(PathSensitiveBugReport *R, + const AllocationPair &AP) const { + R->markInteresting(AP.first); + R->markInteresting(AP.second->Region); + } + + /// The bug visitor which allows us to print extra diagnostics along the + /// BugReport path. For example, showing the allocation site of the leaked + /// region. + class SecKeychainBugVisitor : public BugReporterVisitor { + protected: + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; + + public: + SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Sym); + } + + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + }; +}; +} + +/// ProgramState traits to store the currently allocated (and not yet freed) +/// symbols. This is a map from the allocated content symbol to the +/// corresponding AllocationState. +REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, + SymbolRef, + MacOSKeychainAPIChecker::AllocationState) + +static bool isEnclosingFunctionParam(const Expr *E) { + E = E->IgnoreParenCasts(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + const ValueDecl *VD = DRE->getDecl(); + if (isa<ImplicitParamDecl, ParmVarDecl>(VD)) + return true; + } + return false; +} + +const MacOSKeychainAPIChecker::ADFunctionInfo + MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { + {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 + {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 + {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 + {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 + {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 + {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 + {"free", 0, InvalidIdx, ErrorAPI}, // 6 + {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 +}; + +unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, + bool IsAllocator) { + for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { + ADFunctionInfo FI = FunctionsToTrack[I]; + if (FI.Name != Name) + continue; + // Make sure the function is of the right type (allocator vs deallocator). + if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) + return InvalidIdx; + if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) + return InvalidIdx; + + return I; + } + // The function is not tracked. + return InvalidIdx; +} + +static bool isBadDeallocationArgument(const MemRegion *Arg) { + if (!Arg) + return false; + return isa<AllocaRegion, BlockDataRegion, TypedRegion>(Arg); +} + +/// Given the address expression, retrieve the value it's pointing to. Assume +/// that value is itself an address, and return the corresponding symbol. +static SymbolRef getAsPointeeSymbol(const Expr *Expr, + CheckerContext &C) { + ProgramStateRef State = C.getState(); + SVal ArgV = C.getSVal(Expr); + + if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { + StoreManager& SM = C.getStoreManager(); + SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); + if (sym) + return sym; + } + return nullptr; +} + +// Report deallocator mismatch. Remove the region from tracking - reporting a +// missing free error after this one is redundant. +void MacOSKeychainAPIChecker:: + generateDeallocatorMismatchReport(const AllocationPair &AP, + const Expr *ArgExpr, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = State->remove<AllocatedData>(AP.first); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + + if (!N) + return; + initBugType(); + SmallString<80> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int PDeallocIdx = + FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; + + os << "Deallocator doesn't match the allocator: '" + << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); + Report->addRange(ArgExpr->getSourceRange()); + markInteresting(Report.get(), AP); + C.emitReport(std::move(Report)); +} + +void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + unsigned idx = InvalidIdx; + ProgramStateRef State = C.getState(); + + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); + if (funName.empty()) + return; + + // If it is a call to an allocator function, it could be a double allocation. + idx = getTrackedFunctionIndex(funName, true); + if (idx != InvalidIdx) { + unsigned paramIdx = FunctionsToTrack[idx].Param; + if (CE->getNumArgs() <= paramIdx) + return; + + const Expr *ArgExpr = CE->getArg(paramIdx); + if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) + if (const AllocationState *AS = State->get<AllocatedData>(V)) { + // Remove the value from the state. The new symbol will be added for + // tracking when the second allocator is processed in checkPostStmt(). + State = State->remove<AllocatedData>(V); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + initBugType(); + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + os << "Allocated data should be released before another call to " + << "the allocator: missing a call to '" + << FunctionsToTrack[DIdx].Name + << "'."; + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V)); + Report->addRange(ArgExpr->getSourceRange()); + Report->markInteresting(AS->Region); + C.emitReport(std::move(Report)); + } + return; + } + + // Is it a call to one of deallocator functions? + idx = getTrackedFunctionIndex(funName, false); + if (idx == InvalidIdx) + return; + + unsigned paramIdx = FunctionsToTrack[idx].Param; + if (CE->getNumArgs() <= paramIdx) + return; + + // Check the argument to the deallocator. + const Expr *ArgExpr = CE->getArg(paramIdx); + SVal ArgSVal = C.getSVal(ArgExpr); + + // Undef is reported by another checker. + if (ArgSVal.isUndef()) + return; + + SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); + + // If the argument is coming from the heap, globals, or unknown, do not + // report it. + bool RegionArgIsBad = false; + if (!ArgSM) { + if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) + return; + RegionArgIsBad = true; + } + + // Is the argument to the call being tracked? + const AllocationState *AS = State->get<AllocatedData>(ArgSM); + if (!AS) + return; + + // TODO: We might want to report double free here. + // (that would involve tracking all the freed symbols in the checker state). + if (RegionArgIsBad) { + // It is possible that this is a false positive - the argument might + // have entered as an enclosing function parameter. + if (isEnclosingFunctionParam(ArgExpr)) + return; + + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + initBugType(); + auto Report = std::make_unique<PathSensitiveBugReport>( + *BT, "Trying to free data which has not been allocated.", N); + Report->addRange(ArgExpr->getSourceRange()); + if (AS) + Report->markInteresting(AS->Region); + C.emitReport(std::move(Report)); + return; + } + + // Process functions which might deallocate. + if (FunctionsToTrack[idx].Kind == PossibleAPI) { + + if (funName == "CFStringCreateWithBytesNoCopy") { + const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); + // NULL ~ default deallocator, so warn. + if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + // One of the default allocators, so warn. + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorDefault" || + DeallocatorName == "kCFAllocatorSystemDefault" || + DeallocatorName == "kCFAllocatorMalloc") { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + // If kCFAllocatorNull, which does not deallocate, we still have to + // find the deallocator. + if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") + return; + } + // In all other cases, assume the user supplied a correct deallocator + // that will free memory so stop tracking. + State = State->remove<AllocatedData>(ArgSM); + C.addTransition(State); + return; + } + + llvm_unreachable("We know of no other possible APIs."); + } + + // The call is deallocating a value we previously allocated, so remove it + // from the next state. + State = State->remove<AllocatedData>(ArgSM); + + // Check if the proper deallocator is used. + unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + + C.addTransition(State); +} + +void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); + + // If a value has been allocated, add it to the set for tracking. + unsigned idx = getTrackedFunctionIndex(funName, true); + if (idx == InvalidIdx) + return; + + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); + // If the argument entered as an enclosing function parameter, skip it to + // avoid false positives. + if (isEnclosingFunctionParam(ArgExpr) && + C.getLocationContext()->getParent() == nullptr) + return; + + if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { + // If the argument points to something that's not a symbolic region, it + // can be: + // - unknown (cannot reason about it) + // - undefined (already reported by other checker) + // - constant (null - should not be tracked, + // other constant will generate a compiler warning) + // - goto (should be reported by other checker) + + // The call return value symbol should stay alive for as long as the + // allocated value symbol, since our diagnostics depend on the value + // returned by the call. Ex: Data should only be freed if noErr was + // returned during allocation.) + SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol(); + C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); + + // Track the allocated value in the checker state. + State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, + RetStatusSymbol)); + assert(State); + C.addTransition(State); + } +} + +// TODO: This logic is the same as in Malloc checker. +const ExplodedNode * +MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, + SymbolRef Sym, + CheckerContext &C) const { + const LocationContext *LeakContext = N->getLocationContext(); + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked symbol. + const ExplodedNode *AllocNode = N; + + while (N) { + if (!N->getState()->get<AllocatedData>(Sym)) + break; + // Allocation node, is the last node in the current or parent context in + // which the symbol was tracked. + const LocationContext *NContext = N->getLocationContext(); + if (NContext == LeakContext || + NContext->isParentOf(LeakContext)) + AllocNode = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + + return AllocNode; +} + +std::unique_ptr<PathSensitiveBugReport> +MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( + const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { + const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; + initBugType(); + SmallString<70> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Allocated data is not released: missing a call to '" + << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; + + // Most bug reports are cached at the location where they occurred. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. + PathDiagnosticLocation LocUsedForUniqueing; + const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); + const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics(); + + if (AllocStmt) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, + C.getSourceManager(), + AllocNode->getLocationContext()); + + auto Report = std::make_unique<PathSensitiveBugReport>( + *BT, os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); + + Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); + markInteresting(Report.get(), AP); + return Report; +} + +/// If the return symbol is assumed to be error, remove the allocated info +/// from consideration. +ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, + SVal Cond, + bool Assumption) const { + AllocatedDataTy AMap = State->get<AllocatedData>(); + if (AMap.isEmpty()) + return State; + + auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol()); + if (!CondBSE) + return State; + BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); + if (OpCode != BO_EQ && OpCode != BO_NE) + return State; + + // Match for a restricted set of patterns for cmparison of error codes. + // Note, the comparisons of type '0 == st' are transformed into SymIntExpr. + SymbolRef ReturnSymbol = nullptr; + if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { + const llvm::APInt &RHS = SIE->getRHS(); + bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) || + (OpCode == BO_NE && RHS == NoErr); + if (!Assumption) + ErrorIsReturned = !ErrorIsReturned; + if (ErrorIsReturned) + ReturnSymbol = SIE->getLHS(); + } + + if (ReturnSymbol) + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + if (ReturnSymbol == I->second.Region) + State = State->remove<AllocatedData>(I->first); + } + + return State; +} + +void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + AllocatedDataTy AMap = State->get<AllocatedData>(); + if (AMap.isEmpty()) + return; + + bool Changed = false; + AllocationPairVec Errors; + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + if (!SR.isDead(I->first)) + continue; + + Changed = true; + State = State->remove<AllocatedData>(I->first); + // If the allocated symbol is null do not report. + ConstraintManager &CMgr = State->getConstraintManager(); + ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); + if (AllocFailed.isConstrainedTrue()) + continue; + Errors.push_back(std::make_pair(I->first, &I->second)); + } + if (!Changed) { + // Generate the new, cleaned up state. + C.addTransition(State); + return; + } + + static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); + ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (!N) + return; + + // Generate the error reports. + for (const auto &P : Errors) + C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); + + // Generate the new, cleaned up state. + C.addTransition(State, N); +} + +ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + // FIXME: This branch doesn't make any sense at all, but it is an overfitted + // replacement for a previous overfitted code that was making even less sense. + if (!Call || Call->getDecl()) + return State; + + for (auto I : State->get<AllocatedData>()) { + SymbolRef Sym = I.first; + if (Escaped.count(Sym)) + State = State->remove<AllocatedData>(Sym); + + // This checker is special. Most checkers in fact only track symbols of + // SymbolConjured type, eg. symbols returned from functions such as + // malloc(). This checker tracks symbols returned as out-parameters. + // + // When a function is evaluated conservatively, the out-parameter's pointee + // base region gets invalidated with a SymbolConjured. If the base region is + // larger than the region we're interested in, the value we're interested in + // would be SymbolDerived based on that SymbolConjured. However, such + // SymbolDerived will never be listed in the Escaped set when the base + // region is invalidated because ExprEngine doesn't know which symbols + // were derived from a given symbol, while there can be infinitely many + // valid symbols derived from any given symbol. + // + // Hence the extra boilerplate: remove the derived symbol when its parent + // symbol escapes. + // + if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) { + SymbolRef ParentSym = SD->getParentSymbol(); + if (Escaped.count(ParentSym)) + State = State->remove<AllocatedData>(Sym); + } + } + return State; +} + +PathDiagnosticPieceRef +MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { + const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); + if (!AS) + return nullptr; + const AllocationState *ASPrev = + N->getFirstPred()->getState()->get<AllocatedData>(Sym); + if (ASPrev) + return nullptr; + + // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the + // allocation site. + const CallExpr *CE = + cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); + const FunctionDecl *funDecl = CE->getDirectCallee(); + assert(funDecl && "We do not support indirect function calls as of now."); + StringRef funName = funDecl->getName(); + + // Get the expression of the corresponding argument. + unsigned Idx = getTrackedFunctionIndex(funName, true); + assert(Idx != InvalidIdx && "This should be a call to an allocator."); + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); + PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, + "Data is allocated here."); +} + +void MacOSKeychainAPIChecker::printState(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + const char *Sep) const { + + AllocatedDataTy AMap = State->get<AllocatedData>(); + + if (!AMap.isEmpty()) { + Out << Sep << "KeychainAPIChecker :" << NL; + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + I.getKey()->dumpToStream(Out); + } + } +} + + +void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { + mgr.registerChecker<MacOSKeychainAPIChecker>(); +} + +bool ento::shouldRegisterMacOSKeychainAPIChecker(const CheckerManager &mgr) { + return true; +} |