1 //===-- DataflowAnalysisContext.cpp -----------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a DataflowAnalysisContext class that owns objects that 10 // encompass the state of a program and stores context that is used during 11 // dataflow analysis. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 16 #include "clang/AST/ExprCXX.h" 17 #include "clang/Analysis/FlowSensitive/DebugSupport.h" 18 #include "clang/Analysis/FlowSensitive/Formula.h" 19 #include "clang/Analysis/FlowSensitive/Logger.h" 20 #include "clang/Analysis/FlowSensitive/Value.h" 21 #include "llvm/ADT/SetOperations.h" 22 #include "llvm/ADT/SetVector.h" 23 #include "llvm/Support/CommandLine.h" 24 #include "llvm/Support/Debug.h" 25 #include "llvm/Support/FileSystem.h" 26 #include "llvm/Support/Path.h" 27 #include "llvm/Support/raw_ostream.h" 28 #include <cassert> 29 #include <memory> 30 #include <string> 31 #include <utility> 32 #include <vector> 33 34 static llvm::cl::opt<std::string> DataflowLog( 35 "dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional, 36 llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual " 37 "log to stderr. With an arg, writes HTML logs under the " 38 "specified directory (one per analyzed function).")); 39 40 namespace clang { 41 namespace dataflow { 42 43 FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) { 44 // During context-sensitive analysis, a struct may be allocated in one 45 // function, but its field accessed in a function lower in the stack than 46 // the allocation. Since we only collect fields used in the function where 47 // the allocation occurs, we can't apply that filter when performing 48 // context-sensitive analysis. But, this only applies to storage locations, 49 // since field access it not allowed to fail. In contrast, field *values* 50 // don't need this allowance, since the API allows for uninitialized fields. 51 if (Opts.ContextSensitiveOpts) 52 return getObjectFields(Type); 53 54 return llvm::set_intersection(getObjectFields(Type), ModeledFields); 55 } 56 57 void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) { 58 ModeledFields.set_union(Fields); 59 } 60 61 StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) { 62 if (!Type.isNull() && Type->isRecordType()) { 63 llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs; 64 for (const FieldDecl *Field : getModeledFields(Type)) 65 if (Field->getType()->isReferenceType()) 66 FieldLocs.insert({Field, nullptr}); 67 else 68 FieldLocs.insert({Field, &createStorageLocation( 69 Field->getType().getNonReferenceType())}); 70 return arena().create<RecordStorageLocation>(Type, std::move(FieldLocs)); 71 } 72 return arena().create<ScalarStorageLocation>(Type); 73 } 74 75 StorageLocation & 76 DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) { 77 if (auto *Loc = DeclToLoc.lookup(&D)) 78 return *Loc; 79 auto &Loc = createStorageLocation(D.getType().getNonReferenceType()); 80 DeclToLoc[&D] = &Loc; 81 return Loc; 82 } 83 84 StorageLocation & 85 DataflowAnalysisContext::getStableStorageLocation(const Expr &E) { 86 const Expr &CanonE = ignoreCFGOmittedNodes(E); 87 88 if (auto *Loc = ExprToLoc.lookup(&CanonE)) 89 return *Loc; 90 auto &Loc = createStorageLocation(CanonE.getType()); 91 ExprToLoc[&CanonE] = &Loc; 92 return Loc; 93 } 94 95 PointerValue & 96 DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) { 97 auto CanonicalPointeeType = 98 PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType(); 99 auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr); 100 if (Res.second) { 101 auto &PointeeLoc = createStorageLocation(CanonicalPointeeType); 102 Res.first->second = &arena().create<PointerValue>(PointeeLoc); 103 } 104 return *Res.first->second; 105 } 106 107 void DataflowAnalysisContext::addInvariant(const Formula &Constraint) { 108 if (Invariant == nullptr) 109 Invariant = &Constraint; 110 else 111 Invariant = &arena().makeAnd(*Invariant, Constraint); 112 } 113 114 void DataflowAnalysisContext::addFlowConditionConstraint( 115 Atom Token, const Formula &Constraint) { 116 auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint); 117 if (!Res.second) { 118 Res.first->second = 119 &arena().makeAnd(*Res.first->second, Constraint); 120 } 121 } 122 123 Atom DataflowAnalysisContext::forkFlowCondition(Atom Token) { 124 Atom ForkToken = arena().makeFlowConditionToken(); 125 FlowConditionDeps[ForkToken].insert(Token); 126 addFlowConditionConstraint(ForkToken, arena().makeAtomRef(Token)); 127 return ForkToken; 128 } 129 130 Atom 131 DataflowAnalysisContext::joinFlowConditions(Atom FirstToken, 132 Atom SecondToken) { 133 Atom Token = arena().makeFlowConditionToken(); 134 FlowConditionDeps[Token].insert(FirstToken); 135 FlowConditionDeps[Token].insert(SecondToken); 136 addFlowConditionConstraint(Token, 137 arena().makeOr(arena().makeAtomRef(FirstToken), 138 arena().makeAtomRef(SecondToken))); 139 return Token; 140 } 141 142 Solver::Result DataflowAnalysisContext::querySolver( 143 llvm::SetVector<const Formula *> Constraints) { 144 return S->solve(Constraints.getArrayRef()); 145 } 146 147 bool DataflowAnalysisContext::flowConditionImplies(Atom Token, 148 const Formula &Val) { 149 // Returns true if and only if truth assignment of the flow condition implies 150 // that `Val` is also true. We prove whether or not this property holds by 151 // reducing the problem to satisfiability checking. In other words, we attempt 152 // to show that assuming `Val` is false makes the constraints induced by the 153 // flow condition unsatisfiable. 154 llvm::SetVector<const Formula *> Constraints; 155 Constraints.insert(&arena().makeAtomRef(Token)); 156 Constraints.insert(&arena().makeNot(Val)); 157 addTransitiveFlowConditionConstraints(Token, Constraints); 158 return isUnsatisfiable(std::move(Constraints)); 159 } 160 161 bool DataflowAnalysisContext::flowConditionIsTautology(Atom Token) { 162 // Returns true if and only if we cannot prove that the flow condition can 163 // ever be false. 164 llvm::SetVector<const Formula *> Constraints; 165 Constraints.insert(&arena().makeNot(arena().makeAtomRef(Token))); 166 addTransitiveFlowConditionConstraints(Token, Constraints); 167 return isUnsatisfiable(std::move(Constraints)); 168 } 169 170 bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1, 171 const Formula &Val2) { 172 llvm::SetVector<const Formula *> Constraints; 173 Constraints.insert(&arena().makeNot(arena().makeEquals(Val1, Val2))); 174 return isUnsatisfiable(std::move(Constraints)); 175 } 176 177 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints( 178 Atom Token, llvm::SetVector<const Formula *> &Constraints) { 179 llvm::DenseSet<Atom> AddedTokens; 180 std::vector<Atom> Remaining = {Token}; 181 182 if (Invariant) 183 Constraints.insert(Invariant); 184 // Define all the flow conditions that might be referenced in constraints. 185 while (!Remaining.empty()) { 186 auto Token = Remaining.back(); 187 Remaining.pop_back(); 188 if (!AddedTokens.insert(Token).second) 189 continue; 190 191 auto ConstraintsIt = FlowConditionConstraints.find(Token); 192 if (ConstraintsIt == FlowConditionConstraints.end()) { 193 Constraints.insert(&arena().makeAtomRef(Token)); 194 } else { 195 // Bind flow condition token via `iff` to its set of constraints: 196 // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints 197 Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token), 198 *ConstraintsIt->second)); 199 } 200 201 if (auto DepsIt = FlowConditionDeps.find(Token); 202 DepsIt != FlowConditionDeps.end()) 203 for (Atom A : DepsIt->second) 204 Remaining.push_back(A); 205 } 206 } 207 208 void DataflowAnalysisContext::dumpFlowCondition(Atom Token, 209 llvm::raw_ostream &OS) { 210 llvm::SetVector<const Formula *> Constraints; 211 Constraints.insert(&arena().makeAtomRef(Token)); 212 addTransitiveFlowConditionConstraints(Token, Constraints); 213 214 for (const auto *Constraint : Constraints) { 215 Constraint->print(OS); 216 OS << "\n"; 217 } 218 } 219 220 const ControlFlowContext * 221 DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) { 222 // Canonicalize the key: 223 F = F->getDefinition(); 224 if (F == nullptr) 225 return nullptr; 226 auto It = FunctionContexts.find(F); 227 if (It != FunctionContexts.end()) 228 return &It->second; 229 230 if (F->hasBody()) { 231 auto CFCtx = ControlFlowContext::build(*F); 232 // FIXME: Handle errors. 233 assert(CFCtx); 234 auto Result = FunctionContexts.insert({F, std::move(*CFCtx)}); 235 return &Result.first->second; 236 } 237 238 return nullptr; 239 } 240 241 static std::unique_ptr<Logger> makeLoggerFromCommandLine() { 242 if (DataflowLog.empty()) 243 return Logger::textual(llvm::errs()); 244 245 llvm::StringRef Dir = DataflowLog; 246 if (auto EC = llvm::sys::fs::create_directories(Dir)) 247 llvm::errs() << "Failed to create log dir: " << EC.message() << "\n"; 248 // All analysis runs within a process will log to the same directory. 249 // Share a counter so they don't all overwrite each other's 0.html. 250 // (Don't share a logger, it's not threadsafe). 251 static std::atomic<unsigned> Counter = {0}; 252 auto StreamFactory = 253 [Dir(Dir.str())]() mutable -> std::unique_ptr<llvm::raw_ostream> { 254 llvm::SmallString<256> File(Dir); 255 llvm::sys::path::append(File, 256 std::to_string(Counter.fetch_add(1)) + ".html"); 257 std::error_code EC; 258 auto OS = std::make_unique<llvm::raw_fd_ostream>(File, EC); 259 if (EC) { 260 llvm::errs() << "Failed to create log " << File << ": " << EC.message() 261 << "\n"; 262 return std::make_unique<llvm::raw_null_ostream>(); 263 } 264 return OS; 265 }; 266 return Logger::html(std::move(StreamFactory)); 267 } 268 269 DataflowAnalysisContext::DataflowAnalysisContext(std::unique_ptr<Solver> S, 270 Options Opts) 271 : S(std::move(S)), A(std::make_unique<Arena>()), Opts(Opts) { 272 assert(this->S != nullptr); 273 // If the -dataflow-log command-line flag was set, synthesize a logger. 274 // This is ugly but provides a uniform method for ad-hoc debugging dataflow- 275 // based tools. 276 if (Opts.Log == nullptr) { 277 if (DataflowLog.getNumOccurrences() > 0) { 278 LogOwner = makeLoggerFromCommandLine(); 279 this->Opts.Log = LogOwner.get(); 280 // FIXME: if the flag is given a value, write an HTML log to a file. 281 } else { 282 this->Opts.Log = &Logger::null(); 283 } 284 } 285 } 286 287 DataflowAnalysisContext::~DataflowAnalysisContext() = default; 288 289 } // namespace dataflow 290 } // namespace clang 291 292 using namespace clang; 293 294 const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) { 295 const Expr *Current = &E; 296 if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) { 297 Current = EWC->getSubExpr(); 298 assert(Current != nullptr); 299 } 300 Current = Current->IgnoreParens(); 301 assert(Current != nullptr); 302 return *Current; 303 } 304 305 const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) { 306 if (auto *E = dyn_cast<Expr>(&S)) 307 return ignoreCFGOmittedNodes(*E); 308 return S; 309 } 310 311 // FIXME: Does not precisely handle non-virtual diamond inheritance. A single 312 // field decl will be modeled for all instances of the inherited field. 313 static void getFieldsFromClassHierarchy(QualType Type, 314 clang::dataflow::FieldSet &Fields) { 315 if (Type->isIncompleteType() || Type->isDependentType() || 316 !Type->isRecordType()) 317 return; 318 319 for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) 320 Fields.insert(Field); 321 if (auto *CXXRecord = Type->getAsCXXRecordDecl()) 322 for (const CXXBaseSpecifier &Base : CXXRecord->bases()) 323 getFieldsFromClassHierarchy(Base.getType(), Fields); 324 } 325 326 /// Gets the set of all fields in the type. 327 clang::dataflow::FieldSet clang::dataflow::getObjectFields(QualType Type) { 328 FieldSet Fields; 329 getFieldsFromClassHierarchy(Type, Fields); 330 return Fields; 331 } 332