Index: lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp +++ lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp @@ -235,6 +235,7 @@ static const char *HANDLE_TYPE_NAME = "mx_handle_t"; static const char *SYSCALL_RETURN_TYPE_NAME = "mx_status_t"; static const int MAX_HANDLE_IN_ARRAY = 64; +typedef std::pair AllocInfo; class MagentaHandleChecker : public Checker &Type, StringRef Msg) const; // Utility functions + AllocInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &Ctx) const; // Generate function spec info based on inline annotations in declaration bool generateSpecForFuncionDecl(const FunctionDecl *FuncDecl) const; // Extract magenta related annotation string from declarations @@ -331,12 +334,70 @@ } return false; } + + bool shouldReport(CheckerContext &Ctx) const; +}; + +class MagentaBugVisitor : public BugReporterVisitorImpl { +private: + SymbolRef Sym; + +public: + MagentaBugVisitor(SymbolRef sym) : Sym(sym) {} + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Sym); } + std::shared_ptr VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR); }; } // end anonymous namespace // Handle -> HandleState map REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) +std::shared_ptr +MagentaBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, + BugReporterContext &BRC, BugReport &BR) { + ProgramStateRef State = N->getState(); + ProgramStateRef StatePref = PrevN->getState(); + const HandleState *HSCurrent = State->get(Sym); + const HandleState *HSPrev = StatePref->get(Sym); + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + if (!HSPrev && HSCurrent && HSCurrent->isAllocated()) { + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + const MemRegion *Region = HSCurrent->getRegion(); + if (Region && Region->canPrintPretty()) { + os << "Handle "; + Region->printPretty(os); + os << " allocated here."; + } else { + os << "Handle allocated here."; + } + return std::make_shared(Pos, os.str()); + } + + if (HSPrev && HSCurrent && HSPrev->isAllocated() && HSCurrent->isReleased()) { + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + const MemRegion *Region = HSCurrent->getRegion(); + if (Region && Region->canPrintPretty()) { + os << "Handle "; + Region->printPretty(os); + os << " released here."; + } else { + os << "Handle released here."; + } + return std::make_shared(Pos, os.str()); + } + return nullptr; +} + MagentaHandleChecker::MagentaHandleChecker() { // Initialize the bug types. LeakBugType.reset( @@ -1034,16 +1095,38 @@ StringRef Msg) const { if (!ErrorNode || !Sym) return; - auto R = llvm::make_unique(*Type, Msg, ErrorNode); + PathDiagnosticLocation LocUsedForUniqueing; + const ExplodedNode *AllocNode = nullptr; + const MemRegion *Region = nullptr; + std::tie(AllocNode, Region) = getAllocationSite(ErrorNode, Sym, C); + const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); + if (AllocStmt) { + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + AllocStmt, C.getSourceManager(), AllocNode->getLocationContext()); + } + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + if (Region && Region->canPrintPretty()) { + os << Msg << " pointed to by "; + Region->printPretty(os); + } else { + os << Msg; + } + auto R = llvm::make_unique( + *Type, os.str(), ErrorNode, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); if (Range) R->addRange(*Range); R->markInteresting(Sym); + R->addVisitor(llvm::make_unique(Sym)); C.emitReport(std::move(R)); } void MagentaHandleChecker::reportLeaks(ArrayRef LeakedHandles, CheckerContext &C, ExplodedNode *ErrorNode) const { + if (!shouldReport(C)) + return; for (SymbolRef LeakedHandle : LeakedHandles) { reportBug(LeakedHandle, ErrorNode, C, nullptr, LeakBugType, "Potential leak of handle"); @@ -1053,6 +1136,8 @@ void MagentaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, CheckerContext &C) const { + if (!shouldReport(C)) + return; ExplodedNode *ErrNode = C.generateErrorNode(); if (!ErrNode) return; @@ -1063,6 +1148,8 @@ void MagentaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, CheckerContext &C) const { + if (!shouldReport(C)) + return; ExplodedNode *ErrNode = C.generateErrorNode(); if (!ErrNode) return; @@ -1070,6 +1157,78 @@ "Using a previously released handle"); } +// Looks for functions that are marked as "DONOTREPORT" in the call stack. If +// there exists one, return false to stop reporting a bug. +bool MagentaHandleChecker::shouldReport(CheckerContext &Ctx) const { + const StackFrameContext *SFCtx = Ctx.getStackFrame(); + // Reverse lookup call stack. Similar to StackFrameContext.dump() + for (const LocationContext *CurLCtx = SFCtx; CurLCtx; + CurLCtx = CurLCtx->getParent()) { + const StackFrameContext *CurSFCtx = dyn_cast(CurLCtx); + if (!CurSFCtx) + continue; + + const FunctionDecl *FD = + dyn_cast_or_null(CurSFCtx->getDecl()); + if (!FD) + continue; + // Top-level functions are not evaluated by evalCall. So their annotations + // are not processed. Process the annotation here. + if (!FuncKindMap.count(FD)) { + if (!generateSpecForFuncionDecl(FD)) { + FuncKindMap[FD] = UNANNOTATED_FUNC; + } else { + FuncSpec FS = FuncDeclMap[FD]; + if (FS.RetAction == DONOTREPORT) { + FuncKindMap[FD] = DONOTREPORT_FUNC; + } + } + } + if (FuncKindMap.count(FD) && FuncKindMap[FD] == DONOTREPORT_FUNC) + return false; + } + return true; +} + +// Reverse traverse the exploded graph to look for allocation side information +// for handle "Sym". +AllocInfo MagentaHandleChecker::getAllocationSite(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; + const MemRegion *ReferenceRegion = nullptr; + + ProgramStateRef State = N->getState(); + // When bug type is handle leak, exploded node N does not have state info for + // leaking handle. Get the predecessor of N instead. + if (!State->get(Sym)) { + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + + while (N) { + State = N->getState(); + if (!State->get(Sym)) + break; + // Find the most recent expression bound to the symbol in the current + // context. + if (!ReferenceRegion) { + const HandleState *HS = State->get(Sym); + if (HS) + ReferenceRegion = HS->getRegion(); + } + // 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 AllocInfo(AllocNode, ReferenceRegion); +} + void ento::registerMagentaHandleChecker(CheckerManager &mgr) { mgr.registerChecker(); } Index: test/Analysis/mxhandle.c =================================================================== --- test/Analysis/mxhandle.c +++ test/Analysis/mxhandle.c @@ -210,7 +210,7 @@ void checkLeak01() { mx_handle_t sa, sb; mx_channel_create(0, &sa, &sb); -} // expected-warning {{Potential leak of handle}} +} // expected-warning 2 {{Potential leak of handle pointed to by}} void checkLeak02(int tag) { @@ -219,7 +219,7 @@ if (tag) { mx_handle_close(sa); } - mx_handle_close(sb); // expected-warning {{Potential leak of handle}} + mx_handle_close(sb); // expected-warning {{Potential leak of handle pointed to by 'sa'}} } void checkLeak03(mx_handle_t handle) { @@ -273,7 +273,7 @@ mx_channel_create(0, &sa, &sb); useHandle01(sa); - mx_handle_close(sb); // expected-warning {{Potential leak of handle}} + mx_handle_close(sb); // expected-warning {{Potential leak of handle pointed to by 'sa'}} } void checkLeak08() { @@ -281,7 +281,7 @@ mx_channel_create(0, &sa, &sb); useHandle02(&sa); - mx_handle_close(sb); // expected-warning {{Potential leak of handle}} + mx_handle_close(sb); // expected-warning {{Potential leak of handle pointed to by 'sa'}} } void checkDoubleRelease01(int tag) { @@ -290,7 +290,7 @@ if (tag) { mx_handle_close(sa); } - mx_handle_close(sa); // expected-warning {{Releasing a previously released handle}} + mx_handle_close(sa); // expected-warning {{Releasing a previously released handle pointed to by 'sa'}} mx_handle_close(sb); } @@ -300,7 +300,27 @@ if (tag) { mx_handle_close(sa); } - useHandle01(sa); // expected-warning {{Using a previously released handle}} + useHandle01(sa); // expected-warning {{Using a previously released handle pointed to by 'sa'}} mx_handle_close(sa); mx_handle_close(sb); } + +void checkSuppressWarning01() MX_SYSCALL_PARAM_ATTR(suppress_warning) { + mx_handle_t sa, sb; + if (mx_channel_create(0, &sa, &sb) < 0) { + return; + } + mx_handle_close(sa); // Should not report any bugs here +} + +void checkSuppressWarning02() { + mx_handle_t sa, sb; + if (mx_channel_create(0, &sa, &sb) < 0) { + return; + } + mx_handle_close(sa); // Should not report any bugs here +} + +void checkSuppressWarning03() MX_SYSCALL_PARAM_ATTR(suppress_warning) { + checkSuppressWarning02(); +}