1 //== CheckerContext.h - Context info for path-sensitive checkers--*- 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 CheckerContext that provides contextual info for 10 // path-sensitive checkers. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H 15 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H 16 17 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 19 #include <optional> 20 21 namespace clang { 22 namespace ento { 23 24 class CheckerContext { 25 ExprEngine &Eng; 26 /// The current exploded(symbolic execution) graph node. 27 ExplodedNode *Pred; 28 /// The flag is true if the (state of the execution) has been modified 29 /// by the checker using this context. For example, a new transition has been 30 /// added or a bug report issued. 31 bool Changed; 32 /// The tagged location, which is used to generate all new nodes. 33 const ProgramPoint Location; 34 NodeBuilder &NB; 35 36 public: 37 /// If we are post visiting a call, this flag will be set if the 38 /// call was inlined. In all other cases it will be false. 39 const bool wasInlined; 40 41 CheckerContext(NodeBuilder &builder, 42 ExprEngine &eng, 43 ExplodedNode *pred, 44 const ProgramPoint &loc, 45 bool wasInlined = false) Eng(eng)46 : Eng(eng), 47 Pred(pred), 48 Changed(false), 49 Location(loc), 50 NB(builder), 51 wasInlined(wasInlined) { 52 assert(Pred->getState() && 53 "We should not call the checkers on an empty state."); 54 assert(loc.getTag() && "The ProgramPoint associated with CheckerContext " 55 "must be tagged with the active checker."); 56 } 57 getAnalysisManager()58 AnalysisManager &getAnalysisManager() { 59 return Eng.getAnalysisManager(); 60 } 61 getConstraintManager()62 ConstraintManager &getConstraintManager() { 63 return Eng.getConstraintManager(); 64 } 65 getStoreManager()66 StoreManager &getStoreManager() { 67 return Eng.getStoreManager(); 68 } 69 70 /// Returns the previous node in the exploded graph, which includes 71 /// the state of the program before the checker ran. Note, checkers should 72 /// not retain the node in their state since the nodes might get invalidated. getPredecessor()73 ExplodedNode *getPredecessor() { return Pred; } getLocation()74 const ProgramPoint getLocation() const { return Location; } getState()75 const ProgramStateRef &getState() const { return Pred->getState(); } 76 77 /// Check if the checker changed the state of the execution; ex: added 78 /// a new transition or a bug report. isDifferent()79 bool isDifferent() { return Changed; } 80 81 /// Returns the number of times the current block has been visited 82 /// along the analyzed path. blockCount()83 unsigned blockCount() const { 84 return NB.getContext().blockCount(); 85 } 86 getASTContext()87 ASTContext &getASTContext() { 88 return Eng.getContext(); 89 } 90 getASTContext()91 const ASTContext &getASTContext() const { return Eng.getContext(); } 92 getLangOpts()93 const LangOptions &getLangOpts() const { 94 return Eng.getContext().getLangOpts(); 95 } 96 getLocationContext()97 const LocationContext *getLocationContext() const { 98 return Pred->getLocationContext(); 99 } 100 getStackFrame()101 const StackFrameContext *getStackFrame() const { 102 return Pred->getStackFrame(); 103 } 104 105 /// Return true if the current LocationContext has no caller context. inTopFrame()106 bool inTopFrame() const { return getLocationContext()->inTopFrame(); } 107 getBugReporter()108 BugReporter &getBugReporter() { 109 return Eng.getBugReporter(); 110 } 111 getSourceManager()112 const SourceManager &getSourceManager() { 113 return getBugReporter().getSourceManager(); 114 } 115 getPreprocessor()116 Preprocessor &getPreprocessor() { return getBugReporter().getPreprocessor(); } 117 getSValBuilder()118 SValBuilder &getSValBuilder() { 119 return Eng.getSValBuilder(); 120 } 121 getSymbolManager()122 SymbolManager &getSymbolManager() { 123 return getSValBuilder().getSymbolManager(); 124 } 125 getStateManager()126 ProgramStateManager &getStateManager() { 127 return Eng.getStateManager(); 128 } 129 getCurrentAnalysisDeclContext()130 AnalysisDeclContext *getCurrentAnalysisDeclContext() const { 131 return Pred->getLocationContext()->getAnalysisDeclContext(); 132 } 133 134 /// Get the blockID. getBlockID()135 unsigned getBlockID() const { 136 return NB.getContext().getBlock()->getBlockID(); 137 } 138 139 /// If the given node corresponds to a PostStore program point, 140 /// retrieve the location region as it was uttered in the code. 141 /// 142 /// This utility can be useful for generating extensive diagnostics, for 143 /// example, for finding variables that the given symbol was assigned to. getLocationRegionIfPostStore(const ExplodedNode * N)144 static const MemRegion *getLocationRegionIfPostStore(const ExplodedNode *N) { 145 ProgramPoint L = N->getLocation(); 146 if (std::optional<PostStore> PSL = L.getAs<PostStore>()) 147 return reinterpret_cast<const MemRegion*>(PSL->getLocationValue()); 148 return nullptr; 149 } 150 151 /// Get the value of arbitrary expressions at this point in the path. getSVal(const Stmt * S)152 SVal getSVal(const Stmt *S) const { 153 return Pred->getSVal(S); 154 } 155 getCFGElementRef()156 ConstCFGElementRef getCFGElementRef() const { return Eng.getCFGElementRef(); } 157 158 /// Returns true if the value of \p E is greater than or equal to \p 159 /// Val under unsigned comparison. 160 bool isGreaterOrEqual(const Expr *E, unsigned long long Val); 161 162 /// Returns true if the value of \p E is negative. 163 bool isNegative(const Expr *E); 164 165 /// Generates a new transition in the program state graph 166 /// (ExplodedGraph). Uses the default CheckerContext predecessor node. 167 /// 168 /// @param State The state of the generated node. If not specified, the state 169 /// will not be changed, but the new node will have the checker's tag. 170 /// @param Tag The tag is used to uniquely identify the creation site. If no 171 /// tag is specified, a default tag, unique to the given checker, 172 /// will be used. Tags are used to prevent states generated at 173 /// different sites from caching out. 174 /// NOTE: If the State is unchanged and the Tag is nullptr, this may return a 175 /// node which is not tagged (instead of using the default tag corresponding 176 /// to the active checker). This is arguably a bug and should be fixed. 177 ExplodedNode *addTransition(ProgramStateRef State = nullptr, 178 const ProgramPointTag *Tag = nullptr) { 179 return addTransitionImpl(State ? State : getState(), false, nullptr, Tag); 180 } 181 182 /// Generates a new transition with the given predecessor. 183 /// Allows checkers to generate a chain of nodes. 184 /// 185 /// @param State The state of the generated node. 186 /// @param Pred The transition will be generated from the specified Pred node 187 /// to the newly generated node. 188 /// @param Tag The tag to uniquely identify the creation site. 189 /// NOTE: If the State is unchanged and the Tag is nullptr, this may return a 190 /// node which is not tagged (instead of using the default tag corresponding 191 /// to the active checker). This is arguably a bug and should be fixed. 192 ExplodedNode *addTransition(ProgramStateRef State, ExplodedNode *Pred, 193 const ProgramPointTag *Tag = nullptr) { 194 return addTransitionImpl(State, false, Pred, Tag); 195 } 196 197 /// Generate a sink node. Generating a sink stops exploration of the 198 /// given path. To create a sink node for the purpose of reporting an error, 199 /// checkers should use generateErrorNode() instead. 200 ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, 201 const ProgramPointTag *Tag = nullptr) { 202 return addTransitionImpl(State ? State : getState(), true, Pred, Tag); 203 } 204 205 /// Add a sink node to the current path of execution, halting analysis. 206 void addSink(ProgramStateRef State = nullptr, 207 const ProgramPointTag *Tag = nullptr) { 208 if (!State) 209 State = getState(); 210 addTransition(State, generateSink(State, getPredecessor())); 211 } 212 213 /// Generate a transition to a node that will be used to report 214 /// an error. This node will be a sink. That is, it will stop exploration of 215 /// the given path. 216 /// 217 /// @param State The state of the generated node. 218 /// @param Tag The tag to uniquely identify the creation site. If null, 219 /// the default tag for the checker will be used. 220 ExplodedNode *generateErrorNode(ProgramStateRef State = nullptr, 221 const ProgramPointTag *Tag = nullptr) { 222 return generateSink(State, Pred, 223 (Tag ? Tag : Location.getTag())); 224 } 225 226 /// Generate a transition to a node that will be used to report 227 /// an error. This node will be a sink. That is, it will stop exploration of 228 /// the given path. 229 /// 230 /// @param State The state of the generated node. 231 /// @param Pred The transition will be generated from the specified Pred node 232 /// to the newly generated node. 233 /// @param Tag The tag to uniquely identify the creation site. If null, 234 /// the default tag for the checker will be used. 235 ExplodedNode *generateErrorNode(ProgramStateRef State, 236 ExplodedNode *Pred, 237 const ProgramPointTag *Tag = nullptr) { 238 return generateSink(State, Pred, 239 (Tag ? Tag : Location.getTag())); 240 } 241 242 /// Generate a transition to a node that will be used to report 243 /// an error. This node will not be a sink. That is, exploration will 244 /// continue along this path. 245 /// 246 /// @param State The state of the generated node. 247 /// @param Tag The tag to uniquely identify the creation site. If null, 248 /// the default tag for the checker will be used. 249 ExplodedNode * 250 generateNonFatalErrorNode(ProgramStateRef State = nullptr, 251 const ProgramPointTag *Tag = nullptr) { 252 return addTransition(State, (Tag ? Tag : Location.getTag())); 253 } 254 255 /// Generate a transition to a node that will be used to report 256 /// an error. This node will not be a sink. That is, exploration will 257 /// continue along this path. 258 /// 259 /// @param State The state of the generated node. 260 /// @param Pred The transition will be generated from the specified Pred node 261 /// to the newly generated node. 262 /// @param Tag The tag to uniquely identify the creation site. If null, 263 /// the default tag for the checker will be used. 264 ExplodedNode * 265 generateNonFatalErrorNode(ProgramStateRef State, 266 ExplodedNode *Pred, 267 const ProgramPointTag *Tag = nullptr) { 268 return addTransition(State, Pred, (Tag ? Tag : Location.getTag())); 269 } 270 271 /// Emit the diagnostics report. emitReport(std::unique_ptr<BugReport> R)272 void emitReport(std::unique_ptr<BugReport> R) { 273 Changed = true; 274 Eng.getBugReporter().emitReport(std::move(R)); 275 } 276 277 /// Produce a program point tag that displays an additional path note 278 /// to the user. This is a lightweight alternative to the 279 /// BugReporterVisitor mechanism: instead of visiting the bug report 280 /// node-by-node to restore the sequence of events that led to discovering 281 /// a bug, you can add notes as you add your transitions. 282 /// 283 /// @param Cb Callback with 'BugReporterContext &, BugReport &' parameters. 284 /// @param IsPrunable Whether the note is prunable. It allows BugReporter 285 /// to omit the note from the report if it would make the displayed 286 /// bug path significantly shorter. 287 LLVM_ATTRIBUTE_RETURNS_NONNULL 288 const NoteTag *getNoteTag(NoteTag::Callback &&Cb, bool IsPrunable = false) { 289 return Eng.getDataTags().make<NoteTag>(std::move(Cb), IsPrunable); 290 } 291 292 /// A shorthand version of getNoteTag that doesn't require you to accept 293 /// the 'BugReporterContext' argument when you don't need it. 294 /// 295 /// @param Cb Callback only with 'BugReport &' parameter. 296 /// @param IsPrunable Whether the note is prunable. It allows BugReporter 297 /// to omit the note from the report if it would make the displayed 298 /// bug path significantly shorter. 299 const NoteTag 300 *getNoteTag(std::function<std::string(PathSensitiveBugReport &)> &&Cb, 301 bool IsPrunable = false) { 302 return getNoteTag( 303 [Cb](BugReporterContext &, 304 PathSensitiveBugReport &BR) { return Cb(BR); }, 305 IsPrunable); 306 } 307 308 /// A shorthand version of getNoteTag that doesn't require you to accept 309 /// the arguments when you don't need it. 310 /// 311 /// @param Cb Callback without parameters. 312 /// @param IsPrunable Whether the note is prunable. It allows BugReporter 313 /// to omit the note from the report if it would make the displayed 314 /// bug path significantly shorter. 315 const NoteTag *getNoteTag(std::function<std::string()> &&Cb, 316 bool IsPrunable = false) { 317 return getNoteTag([Cb](BugReporterContext &, 318 PathSensitiveBugReport &) { return Cb(); }, 319 IsPrunable); 320 } 321 322 /// A shorthand version of getNoteTag that accepts a plain note. 323 /// 324 /// @param Note The note. 325 /// @param IsPrunable Whether the note is prunable. It allows BugReporter 326 /// to omit the note from the report if it would make the displayed 327 /// bug path significantly shorter. 328 const NoteTag *getNoteTag(StringRef Note, bool IsPrunable = false) { 329 return getNoteTag( 330 [Note = std::string(Note)](BugReporterContext &, 331 PathSensitiveBugReport &) { return Note; }, 332 IsPrunable); 333 } 334 335 /// A shorthand version of getNoteTag that accepts a lambda with stream for 336 /// note. 337 /// 338 /// @param Cb Callback with 'BugReport &' and 'llvm::raw_ostream &'. 339 /// @param IsPrunable Whether the note is prunable. It allows BugReporter 340 /// to omit the note from the report if it would make the displayed 341 /// bug path significantly shorter. 342 const NoteTag *getNoteTag( 343 std::function<void(PathSensitiveBugReport &BR, llvm::raw_ostream &OS)> &&Cb, 344 bool IsPrunable = false) { 345 return getNoteTag( 346 [Cb](PathSensitiveBugReport &BR) -> std::string { 347 llvm::SmallString<128> Str; 348 llvm::raw_svector_ostream OS(Str); 349 Cb(BR, OS); 350 return std::string(OS.str()); 351 }, 352 IsPrunable); 353 } 354 355 /// Returns the word that should be used to refer to the declaration 356 /// in the report. 357 StringRef getDeclDescription(const Decl *D); 358 359 /// Get the declaration of the called function (path-sensitive). 360 const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; 361 362 /// Get the name of the called function (path-sensitive). 363 StringRef getCalleeName(const FunctionDecl *FunDecl) const; 364 365 /// Get the identifier of the called function (path-sensitive). getCalleeIdentifier(const CallExpr * CE)366 const IdentifierInfo *getCalleeIdentifier(const CallExpr *CE) const { 367 const FunctionDecl *FunDecl = getCalleeDecl(CE); 368 if (FunDecl) 369 return FunDecl->getIdentifier(); 370 else 371 return nullptr; 372 } 373 374 /// Get the name of the called function (path-sensitive). getCalleeName(const CallExpr * CE)375 StringRef getCalleeName(const CallExpr *CE) const { 376 const FunctionDecl *FunDecl = getCalleeDecl(CE); 377 return getCalleeName(FunDecl); 378 } 379 380 /// Returns true if the given function is an externally-visible function in 381 /// the top-level namespace, such as \c malloc. 382 /// 383 /// If a name is provided, the function must additionally match the given 384 /// name. 385 /// 386 /// Note that this also accepts functions from the \c std namespace (because 387 /// headers like <cstdlib> declare them there) and does not check if the 388 /// function is declared as 'extern "C"' or if it uses C++ name mangling. 389 static bool isCLibraryFunction(const FunctionDecl *FD, 390 StringRef Name = StringRef()); 391 392 /// In builds that use source hardening (-D_FORTIFY_SOURCE), many standard 393 /// functions are implemented as macros that expand to calls of hardened 394 /// functions that take additional arguments compared to the "usual" 395 /// variant and perform additional input validation. For example, a `memcpy` 396 /// call may expand to `__memcpy_chk()` or `__builtin___memcpy_chk()`. 397 /// 398 /// This method returns true if `FD` declares a fortified variant of the 399 /// standard library function `Name`. 400 /// 401 /// NOTE: This method relies on heuristics; extend it if you need to handle a 402 /// hardened variant that's not yet covered by it. 403 static bool isHardenedVariantOf(const FunctionDecl *FD, StringRef Name); 404 405 /// Depending on whether the location corresponds to a macro, return 406 /// either the macro name or the token spelling. 407 /// 408 /// This could be useful when checkers' logic depends on whether a function 409 /// is called with a given macro argument. For example: 410 /// s = socket(AF_INET,..) 411 /// If AF_INET is a macro, the result should be treated as a source of taint. 412 /// 413 /// \sa clang::Lexer::getSpelling(), clang::Lexer::getImmediateMacroName(). 414 StringRef getMacroNameOrSpelling(SourceLocation &Loc); 415 416 private: 417 ExplodedNode *addTransitionImpl(ProgramStateRef State, 418 bool MarkAsSink, 419 ExplodedNode *P = nullptr, 420 const ProgramPointTag *Tag = nullptr) { 421 // The analyzer may stop exploring if it sees a state it has previously 422 // visited ("cache out"). The early return here is a defensive check to 423 // prevent accidental caching out by checker API clients. Unless there is a 424 // tag or the client checker has requested that the generated node be 425 // marked as a sink, we assume that a client requesting a transition to a 426 // state that is the same as the predecessor state has made a mistake. We 427 // return the predecessor rather than cache out. 428 // 429 // TODO: We could potentially change the return to an assertion to alert 430 // clients to their mistake, but several checkers (including 431 // DereferenceChecker, CallAndMessageChecker, and DynamicTypePropagation) 432 // rely upon the defensive behavior and would need to be updated. 433 if (!State || (State == Pred->getState() && !Tag && !MarkAsSink)) 434 return Pred; 435 436 Changed = true; 437 const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); 438 if (!P) 439 P = Pred; 440 441 ExplodedNode *node; 442 if (MarkAsSink) 443 node = NB.generateSink(LocalLoc, State, P); 444 else 445 node = NB.generateNode(LocalLoc, State, P); 446 return node; 447 } 448 }; 449 450 } // end GR namespace 451 452 } // end clang namespace 453 454 #endif 455