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