1 //== SValExplainer.h - Symbolic value explainer -----------------*- 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 SValExplainer, a class for pretty-printing a 10 // human-readable description of a symbolic value. For example, 11 // "reg_$0<x>" is turned into "initial value of variable 'x'". 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H 16 #define LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H 17 18 #include "clang/AST/Attr.h" 19 #include "clang/AST/DeclCXX.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" 21 #include "llvm/ADT/StringExtras.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 namespace clang { 25 26 namespace ento { 27 28 class SValExplainer : public FullSValVisitor<SValExplainer, std::string> { 29 private: 30 ASTContext &ACtx; 31 ProgramStateRef State; 32 printCFGElementRef(ConstCFGElementRef Elem)33 std::string printCFGElementRef(ConstCFGElementRef Elem) { 34 std::string Str; 35 llvm::raw_string_ostream OS(Str); 36 Elem->dumpToStream(OS, /*TerminateWithNewLine=*/false); 37 return Str; 38 } 39 printStmt(const Stmt * S)40 std::string printStmt(const Stmt *S) { 41 std::string Str; 42 llvm::raw_string_ostream OS(Str); 43 S->printPretty(OS, nullptr, PrintingPolicy(ACtx.getLangOpts())); 44 return Str; 45 } 46 isThisObject(const SymbolicRegion * R)47 bool isThisObject(const SymbolicRegion *R) { 48 if (auto S = dyn_cast<SymbolRegionValue>(R->getSymbol())) 49 if (isa<CXXThisRegion>(S->getRegion())) 50 return true; 51 return false; 52 } 53 isThisObject(const ElementRegion * R)54 bool isThisObject(const ElementRegion *R) { 55 if (const auto *Idx = R->getIndex().getAsInteger()) { 56 if (const auto *SR = R->getSuperRegion()->getAs<SymbolicRegion>()) { 57 QualType Ty = SR->getPointeeStaticType(); 58 bool IsNotReinterpretCast = R->getValueType() == Ty; 59 if (Idx->isZero() && IsNotReinterpretCast) 60 return isThisObject(SR); 61 } 62 } 63 return false; 64 } 65 66 public: SValExplainer(ASTContext & Ctx,ProgramStateRef State)67 SValExplainer(ASTContext &Ctx, ProgramStateRef State) 68 : ACtx(Ctx), State(State) {} 69 VisitUnknownVal(UnknownVal V)70 std::string VisitUnknownVal(UnknownVal V) { 71 return "unknown value"; 72 } 73 VisitUndefinedVal(UndefinedVal V)74 std::string VisitUndefinedVal(UndefinedVal V) { 75 return "undefined value"; 76 } 77 VisitMemRegionVal(loc::MemRegionVal V)78 std::string VisitMemRegionVal(loc::MemRegionVal V) { 79 const MemRegion *R = V.getRegion(); 80 // Avoid the weird "pointer to pointee of ...". 81 if (auto SR = dyn_cast<SymbolicRegion>(R)) { 82 // However, "pointer to 'this' object" is fine. 83 if (!isThisObject(SR)) 84 return Visit(SR->getSymbol()); 85 } 86 return "pointer to " + Visit(R); 87 } 88 VisitConcreteInt(loc::ConcreteInt V)89 std::string VisitConcreteInt(loc::ConcreteInt V) { 90 const llvm::APSInt &I = V.getValue(); 91 std::string Str; 92 llvm::raw_string_ostream OS(Str); 93 OS << "concrete memory address '" << I << "'"; 94 return Str; 95 } 96 VisitSymbolVal(nonloc::SymbolVal V)97 std::string VisitSymbolVal(nonloc::SymbolVal V) { 98 return Visit(V.getSymbol()); 99 } 100 VisitConcreteInt(nonloc::ConcreteInt V)101 std::string VisitConcreteInt(nonloc::ConcreteInt V) { 102 const llvm::APSInt &I = V.getValue(); 103 std::string Str; 104 llvm::raw_string_ostream OS(Str); 105 OS << (I.isSigned() ? "signed " : "unsigned ") << I.getBitWidth() 106 << "-bit integer '" << I << "'"; 107 return Str; 108 } 109 VisitLazyCompoundVal(nonloc::LazyCompoundVal V)110 std::string VisitLazyCompoundVal(nonloc::LazyCompoundVal V) { 111 return "lazily frozen compound value of " + Visit(V.getRegion()); 112 } 113 VisitSymbolRegionValue(const SymbolRegionValue * S)114 std::string VisitSymbolRegionValue(const SymbolRegionValue *S) { 115 const MemRegion *R = S->getRegion(); 116 // Special handling for argument values. 117 if (auto V = dyn_cast<VarRegion>(R)) 118 if (auto D = dyn_cast<ParmVarDecl>(V->getDecl())) 119 return "argument '" + D->getQualifiedNameAsString() + "'"; 120 return "initial value of " + Visit(R); 121 } 122 VisitSymbolConjured(const SymbolConjured * S)123 std::string VisitSymbolConjured(const SymbolConjured *S) { 124 return "symbol of type '" + S->getType().getAsString() + 125 "' conjured at CFG element '" + 126 printCFGElementRef(S->getCFGElementRef()) + "'"; 127 } 128 VisitSymbolDerived(const SymbolDerived * S)129 std::string VisitSymbolDerived(const SymbolDerived *S) { 130 return "value derived from (" + Visit(S->getParentSymbol()) + 131 ") for " + Visit(S->getRegion()); 132 } 133 VisitSymbolExtent(const SymbolExtent * S)134 std::string VisitSymbolExtent(const SymbolExtent *S) { 135 return "extent of " + Visit(S->getRegion()); 136 } 137 VisitSymbolMetadata(const SymbolMetadata * S)138 std::string VisitSymbolMetadata(const SymbolMetadata *S) { 139 return "metadata of type '" + S->getType().getAsString() + "' tied to " + 140 Visit(S->getRegion()); 141 } 142 VisitSymIntExpr(const SymIntExpr * S)143 std::string VisitSymIntExpr(const SymIntExpr *S) { 144 std::string Str; 145 llvm::raw_string_ostream OS(Str); 146 OS << "(" << Visit(S->getLHS()) << ") " 147 << std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) << " " 148 << S->getRHS(); 149 return Str; 150 } 151 152 // TODO: IntSymExpr doesn't appear in practice. 153 // Add the relevant code once it does. 154 VisitSymSymExpr(const SymSymExpr * S)155 std::string VisitSymSymExpr(const SymSymExpr *S) { 156 return "(" + Visit(S->getLHS()) + ") " + 157 std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) + 158 " (" + Visit(S->getRHS()) + ")"; 159 } 160 VisitUnarySymExpr(const UnarySymExpr * S)161 std::string VisitUnarySymExpr(const UnarySymExpr *S) { 162 return std::string(UnaryOperator::getOpcodeStr(S->getOpcode())) + " (" + 163 Visit(S->getOperand()) + ")"; 164 } 165 166 // TODO: SymbolCast doesn't appear in practice. 167 // Add the relevant code once it does. 168 VisitSymbolicRegion(const SymbolicRegion * R)169 std::string VisitSymbolicRegion(const SymbolicRegion *R) { 170 // Explain 'this' object here - if it's not wrapped by an ElementRegion. 171 // TODO: Explain CXXThisRegion itself, find a way to test it. 172 if (isThisObject(R)) 173 return "'this' object"; 174 // Objective-C objects are not normal symbolic regions. At least, 175 // they're always on the heap. 176 if (R->getSymbol()->getType() 177 .getCanonicalType()->getAs<ObjCObjectPointerType>()) 178 return "object at " + Visit(R->getSymbol()); 179 // Other heap-based symbolic regions are also special. 180 if (R->hasMemorySpace<HeapSpaceRegion>(State)) 181 return "heap segment that starts at " + Visit(R->getSymbol()); 182 return "pointee of " + Visit(R->getSymbol()); 183 } 184 VisitAllocaRegion(const AllocaRegion * R)185 std::string VisitAllocaRegion(const AllocaRegion *R) { 186 return "region allocated by '" + printStmt(R->getExpr()) + "'"; 187 } 188 VisitCompoundLiteralRegion(const CompoundLiteralRegion * R)189 std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) { 190 return "compound literal " + printStmt(R->getLiteralExpr()); 191 } 192 VisitStringRegion(const StringRegion * R)193 std::string VisitStringRegion(const StringRegion *R) { 194 return "string literal " + R->getString(); 195 } 196 VisitElementRegion(const ElementRegion * R)197 std::string VisitElementRegion(const ElementRegion *R) { 198 std::string Str; 199 llvm::raw_string_ostream OS(Str); 200 201 // Explain 'this' object here. 202 // They are represented by a SymRegion wrapped by an ElementRegion; so 203 // match and handle it here. 204 if (isThisObject(R)) 205 return "'this' object"; 206 207 OS << "element of type '" << R->getElementType() << "' with index "; 208 // For concrete index: omit type of the index integer. 209 if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>()) 210 OS << I->getValue(); 211 else 212 OS << "'" << Visit(R->getIndex()) << "'"; 213 OS << " of " + Visit(R->getSuperRegion()); 214 return Str; 215 } 216 VisitNonParamVarRegion(const NonParamVarRegion * R)217 std::string VisitNonParamVarRegion(const NonParamVarRegion *R) { 218 const VarDecl *VD = R->getDecl(); 219 std::string Name = VD->getQualifiedNameAsString(); 220 if (isa<ParmVarDecl>(VD)) 221 return "parameter '" + Name + "'"; 222 else if (VD->hasAttr<BlocksAttr>()) 223 return "block variable '" + Name + "'"; 224 else if (VD->hasLocalStorage()) 225 return "local variable '" + Name + "'"; 226 else if (VD->isStaticLocal()) 227 return "static local variable '" + Name + "'"; 228 else if (VD->hasGlobalStorage()) 229 return "global variable '" + Name + "'"; 230 else 231 llvm_unreachable("A variable is either local or global"); 232 } 233 VisitObjCIvarRegion(const ObjCIvarRegion * R)234 std::string VisitObjCIvarRegion(const ObjCIvarRegion *R) { 235 return "instance variable '" + R->getDecl()->getNameAsString() + "' of " + 236 Visit(R->getSuperRegion()); 237 } 238 VisitFieldRegion(const FieldRegion * R)239 std::string VisitFieldRegion(const FieldRegion *R) { 240 return "field '" + R->getDecl()->getNameAsString() + "' of " + 241 Visit(R->getSuperRegion()); 242 } 243 VisitCXXTempObjectRegion(const CXXTempObjectRegion * R)244 std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) { 245 return "temporary object constructed at statement '" + 246 printStmt(R->getExpr()) + "'"; 247 } 248 VisitCXXBaseObjectRegion(const CXXBaseObjectRegion * R)249 std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) { 250 return "base object '" + R->getDecl()->getQualifiedNameAsString() + 251 "' inside " + Visit(R->getSuperRegion()); 252 } 253 VisitParamVarRegion(const ParamVarRegion * R)254 std::string VisitParamVarRegion(const ParamVarRegion *R) { 255 std::string Str; 256 llvm::raw_string_ostream OS(Str); 257 258 const ParmVarDecl *PVD = R->getDecl(); 259 std::string Name = PVD->getQualifiedNameAsString(); 260 if (!Name.empty()) { 261 OS << "parameter '" << Name << "'"; 262 return std::string(OS.str()); 263 } 264 265 unsigned Index = R->getIndex() + 1; 266 OS << Index << llvm::getOrdinalSuffix(Index) << " parameter of "; 267 const Decl *Parent = R->getStackFrame()->getDecl(); 268 if (const auto *FD = dyn_cast<FunctionDecl>(Parent)) 269 OS << "function '" << FD->getQualifiedNameAsString() << "()'"; 270 else if (const auto *CD = dyn_cast<CXXConstructorDecl>(Parent)) 271 OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'"; 272 else if (const auto *MD = dyn_cast<ObjCMethodDecl>(Parent)) { 273 if (MD->isClassMethod()) 274 OS << "Objective-C method '+" << MD->getQualifiedNameAsString() << "'"; 275 else 276 OS << "Objective-C method '-" << MD->getQualifiedNameAsString() << "'"; 277 } else if (isa<BlockDecl>(Parent)) { 278 if (cast<BlockDecl>(Parent)->isConversionFromLambda()) 279 OS << "lambda"; 280 else 281 OS << "block"; 282 } 283 284 return std::string(OS.str()); 285 } 286 VisitSVal(SVal V)287 std::string VisitSVal(SVal V) { 288 std::string Str; 289 llvm::raw_string_ostream OS(Str); 290 OS << V; 291 return "a value unsupported by the explainer: (" + 292 std::string(OS.str()) + ")"; 293 } 294 VisitSymExpr(SymbolRef S)295 std::string VisitSymExpr(SymbolRef S) { 296 std::string Str; 297 llvm::raw_string_ostream OS(Str); 298 S->dumpToStream(OS); 299 return "a symbolic expression unsupported by the explainer: (" + 300 std::string(OS.str()) + ")"; 301 } 302 VisitMemRegion(const MemRegion * R)303 std::string VisitMemRegion(const MemRegion *R) { 304 std::string Str; 305 llvm::raw_string_ostream OS(Str); 306 OS << R; 307 return "a memory region unsupported by the explainer (" + 308 std::string(OS.str()) + ")"; 309 } 310 }; 311 312 } // end namespace ento 313 314 } // end namespace clang 315 316 #endif 317