================
@@ -1,258 +1,63 @@
-#include "clang/AST/Attr.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
-REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
-REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
-
namespace {
-class UseAfterLifetimeEnd
- : public Checker<check::PostCall, check::EndFunction, check::DeadSymbols> {
+class UseAfterLifetimeEnd : public Checker<check::EndFunction> {
public:
- void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
- const char *Sep) const override;
- void reportDanglingSource(const MemRegion *Region, ExplodedNode *N,
+ void reportDanglingSource(const MemRegion *Source, ExplodedNode *N,
CheckerContext &C) const;
- void checkReturnedBorrower(SVal Val, ProgramStateRef State,
- CheckerContext &C) const;
void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
- void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
};
} // namespace
-static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal,
- const MemRegion *Source) {
- LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
-
- const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
- LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
- Set = F.add(Set, Source);
- State = State->set<LifetimeBoundMap>(RetVal, Set);
- return State;
-}
-
-void UseAfterLifetimeEnd::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
-
- const auto *FC = dyn_cast<AnyFunctionCall>(&Call);
- if (!FC)
- return;
-
- const FunctionDecl *FD = FC->getDecl();
- if (!FD)
- return;
-
- SVal RetVal = Call.getReturnValue();
-
- for (const ParmVarDecl *PVD : FD->parameters()) {
- if (PVD->hasAttr<LifetimeBoundAttr>()) {
- unsigned Idx = PVD->getFunctionScopeIndex();
- SVal Arg = Call.getArgSVal(Idx);
- if (const MemRegion *ArgValRegion = Arg.getAsRegion())
- State = bindValues(State, RetVal, ArgValRegion);
- }
- }
-
- if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) {
- if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
- if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) {
- State = bindValues(State, RetVal, AttrRegion);
- }
- }
- }
- C.addTransition(State);
-}
-
-static bool hasDanglingSource(const MemRegion *Source, ProgramStateRef State,
- CheckerContext &C) {
- // FIXME: The checker currently handles stack-region sources. Other
- // region kinds require separate methodology. For example, heap
- // regions do not go out of scope at the end of a stack frame, so
- // in order to detect those type of dangling sources the function
- // needs to be expanded to an event-driven approach as well.
- if (const auto *StackSpace =
- Source->getMemorySpaceAs<StackSpaceRegion>(State)) {
- const StackFrame *SF = StackSpace->getStackFrame();
- const StackFrame *CurrentSF = C.getStackFrame();
- if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
- return true;
- }
- return false;
-}
-
-void UseAfterLifetimeEnd::checkReturnedBorrower(SVal Val, ProgramStateRef
State,
- CheckerContext &C) const {
- if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
- ExplodedNode *N = nullptr;
- for (const MemRegion *Region : *SourceSet) {
- if (hasDanglingSource(Region, State, C)) {
- if (!N)
- N = C.generateNonFatalErrorNode();
- if (!N)
- return;
- reportDanglingSource(Region, N, C);
- }
- }
- }
-}
-
void UseAfterLifetimeEnd::checkEndFunction(const ReturnStmt *RS,
CheckerContext &C) const {
if (!RS)
return;
ProgramStateRef State = C.getState();
- auto LBMap = State->get<LifetimeBoundMap>();
-
- if (LBMap.isEmpty())
- return;
const Expr *RetExpr = RS->getRetValue();
if (!RetExpr)
return;
RetExpr = RetExpr->IgnoreParens();
SVal RetVal = C.getSVal(RetExpr);
- checkReturnedBorrower(RetVal, State, C);
+ ExplodedNode *N = nullptr;
+
+ std::vector<const MemRegion *> RetValRegion =
+ lifetime_modeling::checkReturnedBorrower(RetVal, State, C);
+ for (const MemRegion *Region : RetValRegion) {
+ if (!N)
+ N = C.generateNonFatalErrorNode();
----------------
Xazax-hun wrote:
I think we might bifurcate the path here if we generate multiple errors. By
default `C.generateNonFatalErrorNode` should add a new node rooted at the
current state. I think there might be an overload that takes a predecessor. If
we generate multiple nodes, the previously generated ones should be the
predecessors to avoid creating a branch in the exploded graph.
https://github.com/llvm/llvm-project/pull/205951
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits