From 2ca72e03c3e690d618319c9563b5e28d10b9f857 Mon Sep 17 00:00:00 2001 From: Henry Wong Date: Wed, 22 Aug 2018 13:30:46 +0000 Subject: [PATCH] [analyzer] Improve `CallDescription` to handle c++ method. Summary: `CallDecription` can only handle function for the time being. If we want to match c++ method, we can only use method name to match and can't improve the matching accuracy through the qualifiers. This patch add the support for `QualifiedName` matching to improve the matching accuracy. Reviewers: xazax.hun, NoQ, george.karpenkov, rnkovacs Reviewed By: xazax.hun, NoQ, rnkovacs Subscribers: Szelethus, szepet, rnkovacs, a.sidorin, mikhail.ramalho, cfe-commits, MTC Differential Revision: https://reviews.llvm.org/D48027 llvm-svn: 340407 --- .../StaticAnalyzer/Core/PathSensitive/CallEvent.h | 23 +++++- .../Checkers/InnerPointerChecker.cpp | 85 ++++++++++------------ clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 29 +++++++- 3 files changed, 85 insertions(+), 52 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 6557f38..f8b2cf7 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -80,7 +80,9 @@ class CallDescription { mutable IdentifierInfo *II = nullptr; mutable bool IsLookupDone = false; - StringRef FuncName; + // The list of the qualified names used to identify the specified CallEvent, + // e.g. "{a, b}" represent the qualified names, like "a::b". + std::vector QualifiedName; unsigned RequiredArgs; public: @@ -88,16 +90,31 @@ public: /// Constructs a CallDescription object. /// + /// @param QualifiedName The list of the qualified names of the function that + /// will be matched. It does not require the user to provide the full list of + /// the qualified name. The more details provided, the more accurate the + /// matching. + /// + /// @param RequiredArgs The number of arguments that is expected to match a + /// call. Omit this parameter to match every occurrence of call with a given + /// name regardless the number of arguments. + CallDescription(std::vector QualifiedName, + unsigned RequiredArgs = NoArgRequirement) + : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs) {} + + /// Constructs a CallDescription object. + /// /// @param FuncName The name of the function that will be matched. /// /// @param RequiredArgs The number of arguments that is expected to match a /// call. Omit this parameter to match every occurrence of call with a given /// name regardless the number of arguments. CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement) - : FuncName(FuncName), RequiredArgs(RequiredArgs) {} + : CallDescription(std::vector({FuncName}), NoArgRequirement) { + } /// Get the name of the function that this object matches. - StringRef getFunctionName() const { return FuncName; } + StringRef getFunctionName() const { return QualifiedName.back(); } }; template diff --git a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index 926a1d7..91805e4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -86,14 +86,20 @@ public: }; InnerPointerChecker() - : AppendFn("append"), AssignFn("assign"), ClearFn("clear"), - CStrFn("c_str"), DataFn("data"), EraseFn("erase"), InsertFn("insert"), - PopBackFn("pop_back"), PushBackFn("push_back"), ReplaceFn("replace"), - ReserveFn("reserve"), ResizeFn("resize"), - ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {} - - /// Check if the object of this member function call is a `basic_string`. - bool isCalledOnStringObject(const CXXInstanceCall *ICall) const; + : AppendFn({"std", "basic_string", "append"}), + AssignFn({"std", "basic_string", "assign"}), + ClearFn({"std", "basic_string", "clear"}), + CStrFn({"std", "basic_string", "c_str"}), + DataFn({"std", "basic_string", "data"}), + EraseFn({"std", "basic_string", "erase"}), + InsertFn({"std", "basic_string", "insert"}), + PopBackFn({"std", "basic_string", "pop_back"}), + PushBackFn({"std", "basic_string", "push_back"}), + ReplaceFn({"std", "basic_string", "replace"}), + ReserveFn({"std", "basic_string", "reserve"}), + ResizeFn({"std", "basic_string", "resize"}), + ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), + SwapFn({"std", "basic_string", "swap"}) {} /// Check whether the called member function potentially invalidates /// pointers referring to the container object's inner buffer. @@ -122,21 +128,6 @@ public: } // end anonymous namespace -bool InnerPointerChecker::isCalledOnStringObject( - const CXXInstanceCall *ICall) const { - const auto *ObjRegion = - dyn_cast_or_null(ICall->getCXXThisVal().getAsRegion()); - if (!ObjRegion) - return false; - - QualType ObjTy = ObjRegion->getValueType(); - if (ObjTy.isNull()) - return false; - - CXXRecordDecl *Decl = ObjTy->getAsCXXRecordDecl(); - return Decl && Decl->getName() == "basic_string"; -} - bool InnerPointerChecker::isInvalidatingMemberFunction( const CallEvent &Call) const { if (const auto *MemOpCall = dyn_cast(&Call)) { @@ -220,33 +211,31 @@ void InnerPointerChecker::checkPostCall(const CallEvent &Call, ProgramStateRef State = C.getState(); if (const auto *ICall = dyn_cast(&Call)) { - if (isCalledOnStringObject(ICall)) { - const auto *ObjRegion = dyn_cast_or_null( - ICall->getCXXThisVal().getAsRegion()); - - if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { - SVal RawPtr = Call.getReturnValue(); - if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { - // Start tracking this raw pointer by adding it to the set of symbols - // associated with this container object in the program state map. - - PtrSet::Factory &F = State->getStateManager().get_context(); - const PtrSet *SetPtr = State->get(ObjRegion); - PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); - assert(C.wasInlined || !Set.contains(Sym)); - Set = F.add(Set, Sym); - - State = State->set(ObjRegion, Set); - C.addTransition(State); - } - return; + const auto *ObjRegion = dyn_cast_or_null( + ICall->getCXXThisVal().getAsRegion()); + + if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { + SVal RawPtr = Call.getReturnValue(); + if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { + // Start tracking this raw pointer by adding it to the set of symbols + // associated with this container object in the program state map. + + PtrSet::Factory &F = State->getStateManager().get_context(); + const PtrSet *SetPtr = State->get(ObjRegion); + PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); + assert(C.wasInlined || !Set.contains(Sym)); + Set = F.add(Set, Sym); + + State = State->set(ObjRegion, Set); + C.addTransition(State); } + return; + } - // Check [string.require] / second point. - if (isInvalidatingMemberFunction(Call)) { - markPtrSymbolsReleased(Call, State, ObjRegion, C); - return; - } + // Check [string.require] / second point. + if (isInvalidatingMemberFunction(Call)) { + markPtrSymbolsReleased(Call, State, ObjRegion, C); + return; } } diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index b168818..3ba68d4 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -359,11 +359,38 @@ bool CallEvent::isCalled(const CallDescription &CD) const { return false; if (!CD.IsLookupDone) { CD.IsLookupDone = true; - CD.II = &getState()->getStateManager().getContext().Idents.get(CD.FuncName); + CD.II = &getState()->getStateManager().getContext().Idents.get( + CD.getFunctionName()); } const IdentifierInfo *II = getCalleeIdentifier(); if (!II || II != CD.II) return false; + + const Decl *D = getDecl(); + // If CallDescription provides prefix names, use them to improve matching + // accuracy. + if (CD.QualifiedName.size() > 1 && D) { + const DeclContext *Ctx = D->getDeclContext(); + std::vector QualifiedName = CD.QualifiedName; + QualifiedName.pop_back(); + for (; Ctx && isa(Ctx); Ctx = Ctx->getParent()) { + if (const auto *ND = dyn_cast(Ctx)) { + if (!QualifiedName.empty() && ND->getName() == QualifiedName.back()) + QualifiedName.pop_back(); + continue; + } + + if (const auto *RD = dyn_cast(Ctx)) { + if (!QualifiedName.empty() && RD->getName() == QualifiedName.back()) + QualifiedName.pop_back(); + continue; + } + } + + if (!QualifiedName.empty()) + return false; + } + return (CD.RequiredArgs == CallDescription::NoArgRequirement || CD.RequiredArgs == getNumArgs()); } -- 2.7.4