1 //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===// 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 defined the Environment and EnvironmentManager classes. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h" 14 #include "clang/AST/Expr.h" 15 #include "clang/AST/ExprCXX.h" 16 #include "clang/AST/PrettyPrinter.h" 17 #include "clang/AST/Stmt.h" 18 #include "clang/AST/StmtObjC.h" 19 #include "clang/Analysis/AnalysisDeclContext.h" 20 #include "clang/Basic/LLVM.h" 21 #include "clang/Basic/LangOptions.h" 22 #include "clang/Basic/JsonSupport.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 28 #include "llvm/ADT/ImmutableMap.h" 29 #include "llvm/ADT/SmallPtrSet.h" 30 #include "llvm/Support/Casting.h" 31 #include "llvm/Support/ErrorHandling.h" 32 #include "llvm/Support/raw_ostream.h" 33 #include <cassert> 34 35 using namespace clang; 36 using namespace ento; 37 38 static const Expr *ignoreTransparentExprs(const Expr *E) { 39 E = E->IgnoreParens(); 40 41 switch (E->getStmtClass()) { 42 case Stmt::OpaqueValueExprClass: 43 E = cast<OpaqueValueExpr>(E)->getSourceExpr(); 44 break; 45 case Stmt::ExprWithCleanupsClass: 46 E = cast<ExprWithCleanups>(E)->getSubExpr(); 47 break; 48 case Stmt::ConstantExprClass: 49 E = cast<ConstantExpr>(E)->getSubExpr(); 50 break; 51 case Stmt::CXXBindTemporaryExprClass: 52 E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); 53 break; 54 case Stmt::SubstNonTypeTemplateParmExprClass: 55 E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); 56 break; 57 default: 58 // This is the base case: we can't look through more than we already have. 59 return E; 60 } 61 62 return ignoreTransparentExprs(E); 63 } 64 65 static const Stmt *ignoreTransparentExprs(const Stmt *S) { 66 if (const auto *E = dyn_cast<Expr>(S)) 67 return ignoreTransparentExprs(E); 68 return S; 69 } 70 71 EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L) 72 : std::pair<const Stmt *, 73 const StackFrameContext *>(ignoreTransparentExprs(S), 74 L ? L->getStackFrame() 75 : nullptr) {} 76 77 SVal Environment::lookupExpr(const EnvironmentEntry &E) const { 78 const SVal* X = ExprBindings.lookup(E); 79 if (X) { 80 SVal V = *X; 81 return V; 82 } 83 return UnknownVal(); 84 } 85 86 SVal Environment::getSVal(const EnvironmentEntry &Entry, 87 SValBuilder& svalBuilder) const { 88 const Stmt *S = Entry.getStmt(); 89 assert(!isa<ObjCForCollectionStmt>(S) && 90 "Use ExprEngine::hasMoreIteration()!"); 91 assert((isa<Expr>(S) || isa<ReturnStmt>(S)) && 92 "Environment can only argue about Exprs, since only they express " 93 "a value! Any non-expression statement stored in Environment is a " 94 "result of a hack!"); 95 const LocationContext *LCtx = Entry.getLocationContext(); 96 97 switch (S->getStmtClass()) { 98 case Stmt::CXXBindTemporaryExprClass: 99 case Stmt::ExprWithCleanupsClass: 100 case Stmt::GenericSelectionExprClass: 101 case Stmt::OpaqueValueExprClass: 102 case Stmt::ConstantExprClass: 103 case Stmt::ParenExprClass: 104 case Stmt::SubstNonTypeTemplateParmExprClass: 105 llvm_unreachable("Should have been handled by ignoreTransparentExprs"); 106 107 case Stmt::AddrLabelExprClass: 108 case Stmt::CharacterLiteralClass: 109 case Stmt::CXXBoolLiteralExprClass: 110 case Stmt::CXXScalarValueInitExprClass: 111 case Stmt::ImplicitValueInitExprClass: 112 case Stmt::IntegerLiteralClass: 113 case Stmt::ObjCBoolLiteralExprClass: 114 case Stmt::CXXNullPtrLiteralExprClass: 115 case Stmt::ObjCStringLiteralClass: 116 case Stmt::StringLiteralClass: 117 case Stmt::TypeTraitExprClass: 118 case Stmt::SizeOfPackExprClass: 119 // Known constants; defer to SValBuilder. 120 return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); 121 122 case Stmt::ReturnStmtClass: { 123 const auto *RS = cast<ReturnStmt>(S); 124 if (const Expr *RE = RS->getRetValue()) 125 return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder); 126 return UndefinedVal(); 127 } 128 129 // Handle all other Stmt* using a lookup. 130 default: 131 return lookupExpr(EnvironmentEntry(S, LCtx)); 132 } 133 } 134 135 Environment EnvironmentManager::bindExpr(Environment Env, 136 const EnvironmentEntry &E, 137 SVal V, 138 bool Invalidate) { 139 if (V.isUnknown()) { 140 if (Invalidate) 141 return Environment(F.remove(Env.ExprBindings, E)); 142 else 143 return Env; 144 } 145 return Environment(F.add(Env.ExprBindings, E, V)); 146 } 147 148 namespace { 149 150 class MarkLiveCallback final : public SymbolVisitor { 151 SymbolReaper &SymReaper; 152 153 public: 154 MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} 155 156 bool VisitSymbol(SymbolRef sym) override { 157 SymReaper.markLive(sym); 158 return true; 159 } 160 161 bool VisitMemRegion(const MemRegion *R) override { 162 SymReaper.markLive(R); 163 return true; 164 } 165 }; 166 167 } // namespace 168 169 // removeDeadBindings: 170 // - Remove subexpression bindings. 171 // - Remove dead block expression bindings. 172 // - Keep live block expression bindings: 173 // - Mark their reachable symbols live in SymbolReaper, 174 // see ScanReachableSymbols. 175 // - Mark the region in DRoots if the binding is a loc::MemRegionVal. 176 Environment 177 EnvironmentManager::removeDeadBindings(Environment Env, 178 SymbolReaper &SymReaper, 179 ProgramStateRef ST) { 180 // We construct a new Environment object entirely, as this is cheaper than 181 // individually removing all the subexpression bindings (which will greatly 182 // outnumber block-level expression bindings). 183 Environment NewEnv = getInitialEnvironment(); 184 185 MarkLiveCallback CB(SymReaper); 186 ScanReachableSymbols RSScaner(ST, CB); 187 188 llvm::ImmutableMapRef<EnvironmentEntry, SVal> 189 EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), 190 F.getTreeFactory()); 191 192 // Iterate over the block-expr bindings. 193 for (Environment::iterator I = Env.begin(), E = Env.end(); 194 I != E; ++I) { 195 const EnvironmentEntry &BlkExpr = I.getKey(); 196 const SVal &X = I.getData(); 197 198 const bool IsBlkExprLive = 199 SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext()); 200 201 assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) && 202 "Only Exprs can be live, LivenessAnalysis argues about the liveness " 203 "of *values*!"); 204 205 if (IsBlkExprLive) { 206 // Copy the binding to the new map. 207 EBMapRef = EBMapRef.add(BlkExpr, X); 208 209 // Mark all symbols in the block expr's value live. 210 RSScaner.scan(X); 211 } 212 } 213 214 NewEnv.ExprBindings = EBMapRef.asImmutableMap(); 215 return NewEnv; 216 } 217 218 void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx, 219 const LocationContext *LCtx, const char *NL, 220 unsigned int Space, bool IsDot) const { 221 Indent(Out, Space, IsDot) << "\"environment\": "; 222 223 if (ExprBindings.isEmpty()) { 224 Out << "null," << NL; 225 return; 226 } 227 228 ++Space; 229 if (!LCtx) { 230 // Find the freshest location context. 231 llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts; 232 for (const auto &I : *this) { 233 const LocationContext *LC = I.first.getLocationContext(); 234 if (FoundContexts.count(LC) == 0) { 235 // This context is fresher than all other contexts so far. 236 LCtx = LC; 237 for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()) 238 FoundContexts.insert(LCI); 239 } 240 } 241 } 242 243 assert(LCtx); 244 245 Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame() 246 << "\", \"items\": [" << NL; 247 PrintingPolicy PP = Ctx.getPrintingPolicy(); 248 249 LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { 250 // LCtx items begin 251 bool HasItem = false; 252 unsigned int InnerSpace = Space + 1; 253 254 // Store the last ExprBinding which we will print. 255 BindingsTy::iterator LastI = ExprBindings.end(); 256 for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); 257 ++I) { 258 if (I->first.getLocationContext() != LC) 259 continue; 260 261 if (!HasItem) { 262 HasItem = true; 263 Out << '[' << NL; 264 } 265 266 const Stmt *S = I->first.getStmt(); 267 (void)S; 268 assert(S != nullptr && "Expected non-null Stmt"); 269 270 LastI = I; 271 } 272 273 for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); 274 ++I) { 275 if (I->first.getLocationContext() != LC) 276 continue; 277 278 const Stmt *S = I->first.getStmt(); 279 Indent(Out, InnerSpace, IsDot) 280 << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": "; 281 S->printJson(Out, nullptr, PP, /*AddQuotes=*/true); 282 283 Out << ", \"value\": "; 284 I->second.printJson(Out, /*AddQuotes=*/true); 285 286 Out << " }"; 287 288 if (I != LastI) 289 Out << ','; 290 Out << NL; 291 } 292 293 if (HasItem) 294 Indent(Out, --InnerSpace, IsDot) << ']'; 295 else 296 Out << "null "; 297 }); 298 299 Indent(Out, --Space, IsDot) << "]}," << NL; 300 } 301