diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -49,6 +49,11 @@ }; /// Holds the state of the program (store and heap) at a given program point. +/// +/// WARNING: Symbolic values that are created by the environment for static +/// local and global variables are not currently invalidated on function calls. +/// This is unsound and should be taken into account when designing dataflow +/// analyses. class Environment { public: /// Supplements `Environment` with non-standard comparison and join diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/ErrorHandling.h" +#include #include #include @@ -56,10 +57,55 @@ return Model.compareEquivalent(Type, *Val1, *Val2); } +/// Initializes a global storage value. +static void initGlobalVar(const VarDecl &D, Environment &Env) { + if (!D.hasGlobalStorage() || + Env.getStorageLocation(D, SkipPast::None) != nullptr) + return; + + auto &Loc = Env.createStorageLocation(D); + Env.setStorageLocation(D, Loc); + if (auto *Val = Env.createValue(D.getType())) + Env.setValue(Loc, *Val); +} + +/// Initializes a global storage value. +static void initGlobalVar(const Decl &D, Environment &Env) { + if (auto *V = dyn_cast(&D)) + initGlobalVar(*V, Env); +} + +/// Initializes global storage values that are declared or referenced from +/// sub-statements of `S`. +// FIXME: Add support for resetting globals after function calls to enable +// the implementation of sound analyses. +static void initGlobalVars(const Stmt &S, Environment &Env) { + for (auto *Child : S.children()) { + if (Child != nullptr) + initGlobalVars(*Child, Env); + } + + if (auto *DS = dyn_cast(&S)) { + if (DS->isSingleDecl()) { + const auto &D = *cast(DS->getSingleDecl()); + initGlobalVar(D, Env); + } else { + for (auto *D : DS->getDeclGroup()) + initGlobalVar(*D, Env); + } + } else if (auto *E = dyn_cast(&S)) { + initGlobalVar(*E->getDecl(), Env); + } else if (auto *E = dyn_cast(&S)) { + initGlobalVar(*E->getMemberDecl(), Env); + } +} + Environment::Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx) : Environment(DACtx) { if (const auto *FuncDecl = dyn_cast(&DeclCtx)) { + assert(FuncDecl->getBody() != nullptr); + initGlobalVars(*FuncDecl->getBody(), *this); for (const auto *ParamDecl : FuncDecl->parameters()) { assert(ParamDecl != nullptr); auto &ParamLoc = createStorageLocation(*ParamDecl); diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -136,6 +136,11 @@ // Group decls are converted into single decls in the CFG so the cast below // is safe. const auto &D = *cast(S->getSingleDecl()); + + // Static local vars are already initialized in `Environment`. + if (D.hasGlobalStorage()) + return; + auto &Loc = Env.createStorageLocation(D); Env.setStorageLocation(D, Loc); @@ -291,6 +296,24 @@ if (Member->isFunctionOrFunctionTemplate()) return; + if (auto *D = dyn_cast(Member)) { + if (D->hasGlobalStorage()) { + auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None); + if (VarDeclLoc == nullptr) + return; + + if (VarDeclLoc->getType()->isReferenceType()) { + Env.setStorageLocation(*S, *VarDeclLoc); + } else { + auto &Loc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, Loc); + Env.setValue(Loc, Env.takeOwnership( + std::make_unique(*VarDeclLoc))); + } + return; + } + } + // The receiver can be either a value or a pointer to a value. Skip past the // indirection to handle both cases. auto *BaseLoc = cast_or_null( diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -2153,4 +2153,171 @@ }); } +TEST_F(TransferTest, StaticIntSingleVarDecl) { + std::string Code = R"( + void target() { + static int Foo; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const StorageLocation *FooLoc = + Env.getStorageLocation(*FooDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(FooLoc)); + + const Value *FooVal = Env.getValue(*FooLoc); + EXPECT_TRUE(isa_and_nonnull(FooVal)); + }); +} + +TEST_F(TransferTest, StaticIntGroupVarDecl) { + std::string Code = R"( + void target() { + static int Foo, Bar; + (void)0; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const StorageLocation *FooLoc = + Env.getStorageLocation(*FooDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(FooLoc)); + + const StorageLocation *BarLoc = + Env.getStorageLocation(*BarDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(BarLoc)); + + const Value *FooVal = Env.getValue(*FooLoc); + EXPECT_TRUE(isa_and_nonnull(FooVal)); + + const Value *BarVal = Env.getValue(*BarLoc); + EXPECT_TRUE(isa_and_nonnull(BarVal)); + + EXPECT_NE(FooVal, BarVal); + }); +} + +TEST_F(TransferTest, GlobalIntVarDecl) { + std::string Code = R"( + static int Foo; + + void target() { + int Bar = Foo; + int Baz = Foo; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + const Value *BarVal = + cast(Env.getValue(*BarDecl, SkipPast::None)); + const Value *BazVal = + cast(Env.getValue(*BazDecl, SkipPast::None)); + EXPECT_EQ(BarVal, BazVal); + }); +} + +TEST_F(TransferTest, StaticMemberIntVarDecl) { + std::string Code = R"( + struct A { + static int Foo; + }; + + void target(A a) { + int Bar = a.Foo; + int Baz = a.Foo; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + const Value *BarVal = + cast(Env.getValue(*BarDecl, SkipPast::None)); + const Value *BazVal = + cast(Env.getValue(*BazDecl, SkipPast::None)); + EXPECT_EQ(BarVal, BazVal); + }); +} + +TEST_F(TransferTest, StaticMemberRefVarDecl) { + std::string Code = R"( + struct A { + static int &Foo; + }; + + void target(A a) { + int Bar = a.Foo; + int Baz = a.Foo; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + const Value *BarVal = + cast(Env.getValue(*BarDecl, SkipPast::None)); + const Value *BazVal = + cast(Env.getValue(*BazDecl, SkipPast::None)); + EXPECT_EQ(BarVal, BazVal); + }); +} + } // namespace