1 @c Copyright (C) 2019-2022 Free Software Foundation, Inc. 2 @c This is part of the GCC manual. 3 @c For copying conditions, see the file gcc.texi. 4 @c Contributed by David Malcolm <dmalcolm (a] redhat.com>. 5 6 @node Static Analyzer 7 @chapter Static Analyzer 8 @cindex analyzer 9 @cindex static analysis 10 @cindex static analyzer 11 12 @menu 13 * Analyzer Internals:: Analyzer Internals 14 * Debugging the Analyzer:: Useful debugging tips 15 @end menu 16 17 @node Analyzer Internals 18 @section Analyzer Internals 19 @cindex analyzer, internals 20 @cindex static analyzer, internals 21 22 @subsection Overview 23 24 The analyzer implementation works on the gimple-SSA representation. 25 (I chose this in the hopes of making it easy to work with LTO to 26 do whole-program analysis). 27 28 The implementation is read-only: it doesn't attempt to change anything, 29 just emit warnings. 30 31 The gimple representation can be seen using @option{-fdump-ipa-analyzer}. 32 @quotation Tip 33 If the analyzer ICEs before this is written out, one workaround is to use 34 @option{--param=analyzer-bb-explosion-factor=0} to force the analyzer 35 to bail out after analyzing the first basic block. 36 @end quotation 37 38 First, we build a @code{supergraph} which combines the callgraph and all 39 of the CFGs into a single directed graph, with both interprocedural and 40 intraprocedural edges. The nodes and edges in the supergraph are called 41 ``supernodes'' and ``superedges'', and often referred to in code as 42 @code{snodes} and @code{sedges}. Basic blocks in the CFGs are split at 43 interprocedural calls, so there can be more than one supernode per 44 basic block. Most statements will be in just one supernode, but a call 45 statement can appear in two supernodes: at the end of one for the call, 46 and again at the start of another for the return. 47 48 The supergraph can be seen using @option{-fdump-analyzer-supergraph}. 49 50 We then build an @code{analysis_plan} which walks the callgraph to 51 determine which calls might be suitable for being summarized (rather 52 than fully explored) and thus in what order to explore the functions. 53 54 Next is the heart of the analyzer: we use a worklist to explore state 55 within the supergraph, building an "exploded graph". 56 Nodes in the exploded graph correspond to <point,@w{ }state> pairs, as in 57 "Precise Interprocedural Dataflow Analysis via Graph Reachability" 58 (Thomas Reps, Susan Horwitz and Mooly Sagiv). 59 60 We reuse nodes for <point, state> pairs we've already seen, and avoid 61 tracking state too closely, so that (hopefully) we rapidly converge 62 on a final exploded graph, and terminate the analysis. We also bail 63 out if the number of exploded <end-of-basic-block, state> nodes gets 64 larger than a particular multiple of the total number of basic blocks 65 (to ensure termination in the face of pathological state-explosion 66 cases, or bugs). We also stop exploring a point once we hit a limit 67 of states for that point. 68 69 We can identify problems directly when processing a <point,@w{ }state> 70 instance. For example, if we're finding the successors of 71 72 @smallexample 73 <point: before-stmt: "free (ptr);", 74 state: @{"ptr": freed@}> 75 @end smallexample 76 77 then we can detect a double-free of "ptr". We can then emit a path 78 to reach the problem by finding the simplest route through the graph. 79 80 Program points in the analysis are much more fine-grained than in the 81 CFG and supergraph, with points (and thus potentially exploded nodes) 82 for various events, including before individual statements. 83 By default the exploded graph merges multiple consecutive statements 84 in a supernode into one exploded edge to minimize the size of the 85 exploded graph. This can be suppressed via 86 @option{-fanalyzer-fine-grained}. 87 The fine-grained approach seems to make things simpler and more debuggable 88 that other approaches I tried, in that each point is responsible for one 89 thing. 90 91 Program points in the analysis also have a "call string" identifying the 92 stack of callsites below them, so that paths in the exploded graph 93 correspond to interprocedurally valid paths: we always return to the 94 correct call site, propagating state information accordingly. 95 We avoid infinite recursion by stopping the analysis if a callsite 96 appears more than @code{analyzer-max-recursion-depth} in a callstring 97 (defaulting to 2). 98 99 @subsection Graphs 100 101 Nodes and edges in the exploded graph are called ``exploded nodes'' and 102 ``exploded edges'' and often referred to in the code as 103 @code{enodes} and @code{eedges} (especially when distinguishing them 104 from the @code{snodes} and @code{sedges} in the supergraph). 105 106 Each graph numbers its nodes, giving unique identifiers - supernodes 107 are referred to throughout dumps in the form @samp{SN': @var{index}} and 108 exploded nodes in the form @samp{EN: @var{index}} (e.g. @samp{SN: 2} and 109 @samp{EN:29}). 110 111 The supergraph can be seen using @option{-fdump-analyzer-supergraph-graph}. 112 113 The exploded graph can be seen using @option{-fdump-analyzer-exploded-graph} 114 and other dump options. Exploded nodes are color-coded in the .dot output 115 based on state-machine states to make it easier to see state changes at 116 a glance. 117 118 @subsection State Tracking 119 120 There's a tension between: 121 @itemize @bullet 122 @item 123 precision of analysis in the straight-line case, vs 124 @item 125 exponential blow-up in the face of control flow. 126 @end itemize 127 128 For example, in general, given this CFG: 129 130 @smallexample 131 A 132 / \ 133 B C 134 \ / 135 D 136 / \ 137 E F 138 \ / 139 G 140 @end smallexample 141 142 we want to avoid differences in state-tracking in B and C from 143 leading to blow-up. If we don't prevent state blowup, we end up 144 with exponential growth of the exploded graph like this: 145 146 @smallexample 147 148 1:A 149 / \ 150 / \ 151 / \ 152 2:B 3:C 153 | | 154 4:D 5:D (2 exploded nodes for D) 155 / \ / \ 156 6:E 7:F 8:E 9:F 157 | | | | 158 10:G 11:G 12:G 13:G (4 exploded nodes for G) 159 160 @end smallexample 161 162 Similar issues arise with loops. 163 164 To prevent this, we follow various approaches: 165 166 @enumerate a 167 @item 168 state pruning: which tries to discard state that won't be relevant 169 later on withing the function. 170 This can be disabled via @option{-fno-analyzer-state-purge}. 171 172 @item 173 state merging. We can try to find the commonality between two 174 program_state instances to make a third, simpler program_state. 175 We have two strategies here: 176 177 @enumerate 178 @item 179 the worklist keeps new nodes for the same program_point together, 180 and tries to merge them before processing, and thus before they have 181 successors. Hence, in the above, the two nodes for D (4 and 5) reach 182 the front of the worklist together, and we create a node for D with 183 the merger of the incoming states. 184 185 @item 186 try merging with the state of existing enodes for the program_point 187 (which may have already been explored). There will be duplication, 188 but only one set of duplication; subsequent duplicates are more likely 189 to hit the cache. In particular, (hopefully) all merger chains are 190 finite, and so we guarantee termination. 191 This is intended to help with loops: we ought to explore the first 192 iteration, and then have a "subsequent iterations" exploration, 193 which uses a state merged from that of the first, to be more abstract. 194 @end enumerate 195 196 We avoid merging pairs of states that have state-machine differences, 197 as these are the kinds of differences that are likely to be most 198 interesting. So, for example, given: 199 200 @smallexample 201 if (condition) 202 ptr = malloc (size); 203 else 204 ptr = local_buf; 205 206 .... do things with 'ptr' 207 208 if (condition) 209 free (ptr); 210 211 ...etc 212 @end smallexample 213 214 then we end up with an exploded graph that looks like this: 215 216 @smallexample 217 218 if (condition) 219 / T \ F 220 --------- ---------- 221 / \ 222 ptr = malloc (size) ptr = local_buf 223 | | 224 copy of copy of 225 "do things with 'ptr'" "do things with 'ptr'" 226 with ptr: heap-allocated with ptr: stack-allocated 227 | | 228 if (condition) if (condition) 229 | known to be T | known to be F 230 free (ptr); | 231 \ / 232 ----------------------------- 233 | ('ptr' is pruned, so states can be merged) 234 etc 235 236 @end smallexample 237 238 where some duplication has occurred, but only for the places where the 239 the different paths are worth exploringly separately. 240 241 Merging can be disabled via @option{-fno-analyzer-state-merge}. 242 @end enumerate 243 244 @subsection Region Model 245 246 Part of the state stored at a @code{exploded_node} is a @code{region_model}. 247 This is an implementation of the region-based ternary model described in 248 @url{https://www.researchgate.net/publication/221430855_A_Memory_Model_for_Static_Analysis_of_C_Programs, 249 "A Memory Model for Static Analysis of C Programs"} 250 (Zhongxing Xu, Ted Kremenek, and Jian Zhang). 251 252 A @code{region_model} encapsulates a representation of the state of 253 memory, with a @code{store} recording a binding between @code{region} 254 instances, to @code{svalue} instances. The bindings are organized into 255 clusters, where regions accessible via well-defined pointer arithmetic 256 are in the same cluster. The representation is graph-like because values 257 can be pointers to regions. It also stores a constraint_manager, 258 capturing relationships between the values. 259 260 Because each node in the @code{exploded_graph} has a @code{region_model}, 261 and each of the latter is graph-like, the @code{exploded_graph} is in some 262 ways a graph of graphs. 263 264 Here's an example of printing a @code{program_state}, showing the 265 @code{region_model} within it, along with state for the @code{malloc} 266 state machine. 267 268 @smallexample 269 (gdb) call debug (*this) 270 rmodel: 271 stack depth: 1 272 frame (index 0): frame: test@@1 273 clusters within frame: test@@1 274 cluster for: ptr_3: &HEAP_ALLOCATED_REGION(12) 275 m_called_unknown_fn: FALSE 276 constraint_manager: 277 equiv classes: 278 constraints: 279 malloc: 280 0x2e89590: &HEAP_ALLOCATED_REGION(12): unchecked ('ptr_3') 281 @end smallexample 282 283 This is the state at the point of returning from @code{calls_malloc} back 284 to @code{test} in the following: 285 286 @smallexample 287 void * 288 calls_malloc (void) 289 @{ 290 void *result = malloc (1024); 291 return result; 292 @} 293 294 void test (void) 295 @{ 296 void *ptr = calls_malloc (); 297 /* etc. */ 298 @} 299 @end smallexample 300 301 Within the store, there is the cluster for @code{ptr_3} within the frame 302 for @code{test}, where the whole cluster is bound to a pointer value, 303 pointing at @code{HEAP_ALLOCATED_REGION(12)}. Additionally, this pointer 304 has the @code{unchecked} state for the @code{malloc} state machine 305 indicating it hasn't yet been checked against NULL since the allocation 306 call. 307 308 @subsection Analyzer Paths 309 310 We need to explain to the user what the problem is, and to persuade them 311 that there really is a problem. Hence having a @code{diagnostic_path} 312 isn't just an incidental detail of the analyzer; it's required. 313 314 Paths ought to be: 315 @itemize @bullet 316 @item 317 interprocedurally-valid 318 @item 319 feasible 320 @end itemize 321 322 Without state-merging, all paths in the exploded graph are feasible 323 (in terms of constraints being satisfied). 324 With state-merging, paths in the exploded graph can be infeasible. 325 326 We collate warnings and only emit them for the simplest path 327 e.g. for a bug in a utility function, with lots of routes to calling it, 328 we only emit the simplest path (which could be intraprocedural, if 329 it can be reproduced without a caller). 330 331 We thus want to find the shortest feasible path through the exploded 332 graph from the origin to the exploded node at which the diagnostic was 333 saved. Unfortunately, if we simply find the shortest such path and 334 check if it's feasible we might falsely reject the diagnostic, as there 335 might be a longer path that is feasible. Examples include the cases 336 where the diagnostic requires us to go at least once around a loop for a 337 later condition to be satisfied, or where for a later condition to be 338 satisfied we need to enter a suite of code that the simpler path skips. 339 340 We attempt to find the shortest feasible path to each diagnostic by 341 first constructing a ``trimmed graph'' from the exploded graph, 342 containing only those nodes and edges from which there are paths to 343 the target node, and using Dijkstra's algorithm to order the trimmed 344 nodes by minimal distance to the target. 345 346 We then use a worklist to iteratively build a ``feasible graph'' 347 (actually a tree), capturing the pertinent state along each path, in 348 which every path to a ``feasible node'' is feasible by construction, 349 restricting ourselves to the trimmed graph to ensure we stay on target, 350 and ordering the worklist so that the first feasible path we find to the 351 target node is the shortest possible path. Hence we start by trying the 352 shortest possible path, but if that fails, we explore progressively 353 longer paths, eventually trying iterations through loops. The 354 exploration is captured in the feasible_graph, which can be dumped as a 355 .dot file via @option{-fdump-analyzer-feasibility} to visualize the 356 exploration. The indices of the feasible nodes show the order in which 357 they were created. We effectively explore the tree of feasible paths in 358 order of shortest path until we either find a feasible path to the 359 target node, or hit a limit and give up. 360 361 This is something of a brute-force approach, but the trimmed graph 362 hopefully keeps the complexity manageable. 363 364 This algorithm can be disabled (for debugging purposes) via 365 @option{-fno-analyzer-feasibility}, which simply uses the shortest path, 366 and notes if it is infeasible. 367 368 The above gives us a shortest feasible @code{exploded_path} through the 369 @code{exploded_graph} (a list of @code{exploded_edge *}). We use this 370 @code{exploded_path} to build a @code{diagnostic_path} (a list of 371 @strong{events} for the diagnostic subsystem) - specifically a 372 @code{checker_path}. 373 374 Having built the @code{checker_path}, we prune it to try to eliminate 375 events that aren't relevant, to minimize how much the user has to read. 376 377 After pruning, we notify each event in the path of its ID and record the 378 IDs of interesting events, allowing for events to refer to other events 379 in their descriptions. The @code{pending_diagnostic} class has various 380 vfuncs to support emitting more precise descriptions, so that e.g. 381 382 @itemize @bullet 383 @item 384 a deref-of-unchecked-malloc diagnostic might use: 385 @smallexample 386 returning possibly-NULL pointer to 'make_obj' from 'allocator' 387 @end smallexample 388 for a @code{return_event} to make it clearer how the unchecked value moves 389 from callee back to caller 390 @item 391 a double-free diagnostic might use: 392 @smallexample 393 second 'free' here; first 'free' was at (3) 394 @end smallexample 395 and a use-after-free might use 396 @smallexample 397 use after 'free' here; memory was freed at (2) 398 @end smallexample 399 @end itemize 400 401 At this point we can emit the diagnostic. 402 403 @subsection Limitations 404 405 @itemize @bullet 406 @item 407 Only for C so far 408 @item 409 The implementation of call summaries is currently very simplistic. 410 @item 411 Lack of function pointer analysis 412 @item 413 The constraint-handling code assumes reflexivity in some places 414 (that values are equal to themselves), which is not the case for NaN. 415 As a simple workaround, constraints on floating-point values are 416 currently ignored. 417 @item 418 There are various other limitations in the region model (grep for TODO/xfail 419 in the testsuite). 420 @item 421 The constraint_manager's implementation of transitivity is currently too 422 expensive to enable by default and so must be manually enabled via 423 @option{-fanalyzer-transitivity}). 424 @item 425 The checkers are currently hardcoded and don't allow for user extensibility 426 (e.g. adding allocate/release pairs). 427 @item 428 Although the analyzer's test suite has a proof-of-concept test case for 429 LTO, LTO support hasn't had extensive testing. There are various 430 lang-specific things in the analyzer that assume C rather than LTO. 431 For example, SSA names are printed to the user in ``raw'' form, rather 432 than printing the underlying variable name. 433 @end itemize 434 435 @node Debugging the Analyzer 436 @section Debugging the Analyzer 437 @cindex analyzer, debugging 438 @cindex static analyzer, debugging 439 440 @subsection Special Functions for Debugging the Analyzer 441 442 The analyzer recognizes various special functions by name, for use 443 in debugging the analyzer. Declarations can be seen in the testsuite 444 in @file{analyzer-decls.h}. None of these functions are actually 445 implemented. 446 447 Add: 448 @smallexample 449 __analyzer_break (); 450 @end smallexample 451 to the source being analyzed to trigger a breakpoint in the analyzer when 452 that source is reached. By putting a series of these in the source, it's 453 much easier to effectively step through the program state as it's analyzed. 454 455 The analyzer handles: 456 457 @smallexample 458 __analyzer_describe (0, expr); 459 @end smallexample 460 461 by emitting a warning describing the 2nd argument (which can be of any 462 type), at a verbosity level given by the 1st argument. This is for use when 463 debugging, and may be of use in DejaGnu tests. 464 465 @smallexample 466 __analyzer_dump (); 467 @end smallexample 468 469 will dump the copious information about the analyzer's state each time it 470 reaches the call in its traversal of the source. 471 472 @smallexample 473 extern void __analyzer_dump_capacity (const void *ptr); 474 @end smallexample 475 476 will emit a warning describing the capacity of the base region of 477 the region pointed to by the 1st argument. 478 479 @smallexample 480 extern void __analyzer_dump_escaped (void); 481 @end smallexample 482 483 will emit a warning giving the number of decls that have escaped on this 484 analysis path, followed by a comma-separated list of their names, 485 in alphabetical order. 486 487 @smallexample 488 __analyzer_dump_path (); 489 @end smallexample 490 491 will emit a placeholder ``note'' diagnostic with a path to that call site, 492 if the analyzer finds a feasible path to it. 493 494 The builtin @code{__analyzer_dump_exploded_nodes} will emit a warning 495 after analysis containing information on all of the exploded nodes at that 496 program point: 497 498 @smallexample 499 __analyzer_dump_exploded_nodes (0); 500 @end smallexample 501 502 will output the number of ``processed'' nodes, and the IDs of 503 both ``processed'' and ``merger'' nodes, such as: 504 505 @smallexample 506 warning: 2 processed enodes: [EN: 56, EN: 58] merger(s): [EN: 54-55, EN: 57, EN: 59] 507 @end smallexample 508 509 With a non-zero argument 510 511 @smallexample 512 __analyzer_dump_exploded_nodes (1); 513 @end smallexample 514 515 it will also dump all of the states within the ``processed'' nodes. 516 517 @smallexample 518 __analyzer_dump_region_model (); 519 @end smallexample 520 will dump the region_model's state to stderr. 521 522 @smallexample 523 __analyzer_dump_state ("malloc", ptr); 524 @end smallexample 525 526 will emit a warning describing the state of the 2nd argument 527 (which can be of any type) with respect to the state machine with 528 a name matching the 1st argument (which must be a string literal). 529 This is for use when debugging, and may be of use in DejaGnu tests. 530 531 @smallexample 532 __analyzer_eval (expr); 533 @end smallexample 534 will emit a warning with text "TRUE", FALSE" or "UNKNOWN" based on the 535 truthfulness of the argument. This is useful for writing DejaGnu tests. 536 537 538 @subsection Other Debugging Techniques 539 540 The option @option{-fdump-analyzer-json} will dump both the supergraph 541 and the exploded graph in compressed JSON form. 542 543 One approach when tracking down where a particular bogus state is 544 introduced into the @code{exploded_graph} is to add custom code to 545 @code{program_state::validate}. 546 547 The debug function @code{region::is_named_decl_p} can be used when debugging, 548 such as for assertions and conditional breakpoints. For example, when 549 tracking down a bug in handling a decl called @code{yy_buffer_stack}, I 550 temporarily added a: 551 @smallexample 552 gcc_assert (!m_base_region->is_named_decl_p ("yy_buffer_stack")); 553 @end smallexample 554 to @code{binding_cluster::mark_as_escaped} to trap a point where 555 @code{yy_buffer_stack} was mistakenly being treated as having escaped. 556