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