Bug Summary

File:build/gcc/analyzer/program-state.cc
Warning:line 857, column 24
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-suse-linux -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name program-state.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/buildworker/marxinbox-gcc-clang-static-analyzer/objdir/gcc -resource-dir /usr/lib64/clang/15.0.7 -D IN_GCC -D HAVE_CONFIG_H -I . -I analyzer -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../include -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libcpp/include -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libcody -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libdecnumber -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libdecnumber/bid -I ../libdecnumber -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libbacktrace -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../include/c++/13/x86_64-suse-linux -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../include/c++/13/backward -internal-isystem /usr/lib64/clang/15.0.7/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../x86_64-suse-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-narrowing -Wwrite-strings -fdeprecated-macro -fdebug-compilation-dir=/buildworker/marxinbox-gcc-clang-static-analyzer/objdir/gcc -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=plist-html -analyzer-config silence-checkers=core.NullDereference -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /buildworker/marxinbox-gcc-clang-static-analyzer/objdir/clang-static-analyzer/2023-03-27-141847-20772-1/report-3XomTI.plist -x c++ /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc
1/* Classes for representing the state of interest at a given path of analysis.
2 Copyright (C) 2019-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#define INCLUDE_MEMORY
23#include "system.h"
24#include "coretypes.h"
25#include "tree.h"
26#include "diagnostic-core.h"
27#include "diagnostic.h"
28#include "analyzer/analyzer.h"
29#include "analyzer/analyzer-logging.h"
30#include "analyzer/sm.h"
31#include "sbitmap.h"
32#include "bitmap.h"
33#include "ordered-hash-map.h"
34#include "selftest.h"
35#include "analyzer/call-string.h"
36#include "analyzer/program-point.h"
37#include "analyzer/store.h"
38#include "analyzer/region-model.h"
39#include "analyzer/program-state.h"
40#include "analyzer/constraint-manager.h"
41#include "diagnostic-event-id.h"
42#include "analyzer/pending-diagnostic.h"
43#include "analyzer/diagnostic-manager.h"
44#include "cfg.h"
45#include "basic-block.h"
46#include "gimple.h"
47#include "gimple-iterator.h"
48#include "cgraph.h"
49#include "digraph.h"
50#include "analyzer/supergraph.h"
51#include "analyzer/program-state.h"
52#include "analyzer/exploded-graph.h"
53#include "analyzer/state-purge.h"
54#include "analyzer/call-summary.h"
55#include "analyzer/analyzer-selftests.h"
56
57#if ENABLE_ANALYZER1
58
59namespace ana {
60
61/* class extrinsic_state. */
62
63/* Dump a multiline representation of this state to PP. */
64
65void
66extrinsic_state::dump_to_pp (pretty_printer *pp) const
67{
68 pp_printf (pp, "extrinsic_state: %i checker(s)\n", get_num_checkers ());
69 unsigned i;
70 state_machine *checker;
71 FOR_EACH_VEC_ELT (m_checkers, i, checker)for (i = 0; (m_checkers).iterate ((i), &(checker)); ++(i)
)
72 {
73 pp_printf (pp, "m_checkers[%i]: %qs\n", i, checker->get_name ());
74 checker->dump_to_pp (pp);
75 }
76}
77
78/* Dump a multiline representation of this state to OUTF. */
79
80void
81extrinsic_state::dump_to_file (FILE *outf) const
82{
83 pretty_printer pp;
84 if (outf == stderrstderr)
85 pp_show_color (&pp)(&pp)->show_color = pp_show_color (global_dc->printer)(global_dc->printer)->show_color;
86 pp.buffer->stream = outf;
87 dump_to_pp (&pp);
88 pp_flush (&pp);
89}
90
91/* Dump a multiline representation of this state to stderr. */
92
93DEBUG_FUNCTION__attribute__ ((__used__)) void
94extrinsic_state::dump () const
95{
96 dump_to_file (stderrstderr);
97}
98
99/* Return a new json::object of the form
100 {"checkers" : array of objects, one for each state_machine}. */
101
102json::object *
103extrinsic_state::to_json () const
104{
105 json::object *ext_state_obj = new json::object ();
106
107 {
108 json::array *checkers_arr = new json::array ();
109 unsigned i;
110 state_machine *sm;
111 FOR_EACH_VEC_ELT (m_checkers, i, sm)for (i = 0; (m_checkers).iterate ((i), &(sm)); ++(i))
112 checkers_arr->append (sm->to_json ());
113 ext_state_obj->set ("checkers", checkers_arr);
114 }
115
116 return ext_state_obj;
117}
118
119/* Get the region_model_manager for this extrinsic_state. */
120
121region_model_manager *
122extrinsic_state::get_model_manager () const
123{
124 if (m_engine)
125 return m_engine->get_model_manager ();
126 else
127 return NULLnullptr; /* for selftests. */
128}
129
130/* Try to find a state machine named NAME.
131 If found, return true and write its index to *OUT.
132 Otherwise return false. */
133
134bool
135extrinsic_state::get_sm_idx_by_name (const char *name, unsigned *out) const
136{
137 unsigned i;
138 state_machine *sm;
139 FOR_EACH_VEC_ELT (m_checkers, i, sm)for (i = 0; (m_checkers).iterate ((i), &(sm)); ++(i))
140 if (0 == strcmp (name, sm->get_name ()))
141 {
142 /* Found NAME. */
143 *out = i;
144 return true;
145 }
146
147 /* NAME not found. */
148 return false;
149}
150
151/* struct sm_state_map::entry_t. */
152
153int
154sm_state_map::entry_t::cmp (const entry_t &entry_a, const entry_t &entry_b)
155{
156 gcc_assert (entry_a.m_state)((void)(!(entry_a.m_state) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 156, __FUNCTION__), 0 : 0))
;
157 gcc_assert (entry_b.m_state)((void)(!(entry_b.m_state) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 157, __FUNCTION__), 0 : 0))
;
158 if (int cmp_state = ((int)entry_a.m_state->get_id ()
159 - (int)entry_b.m_state->get_id ()))
160 return cmp_state;
161 if (entry_a.m_origin && entry_b.m_origin)
162 return svalue::cmp_ptr (entry_a.m_origin, entry_b.m_origin);
163 if (entry_a.m_origin)
164 return 1;
165 if (entry_b.m_origin)
166 return -1;
167 return 0;
168}
169
170/* class sm_state_map. */
171
172/* sm_state_map's ctor. */
173
174sm_state_map::sm_state_map (const state_machine &sm)
175: m_sm (sm), m_map (), m_global_state (sm.get_start_state ())
176{
177}
178
179/* Clone the sm_state_map. */
180
181sm_state_map *
182sm_state_map::clone () const
183{
184 return new sm_state_map (*this);
185}
186
187/* Print this sm_state_map to PP.
188 If MODEL is non-NULL, print representative tree values where
189 available. */
190
191void
192sm_state_map::print (const region_model *model,
193 bool simple, bool multiline,
194 pretty_printer *pp) const
195{
196 bool first = true;
197 if (!multiline)
198 pp_string (pp, "{");
199 if (m_global_state != m_sm.get_start_state ())
200 {
201 if (multiline)
202 pp_string (pp, " ");
203 pp_string (pp, "global: ");
204 m_global_state->dump_to_pp (pp);
205 if (multiline)
206 pp_newline (pp);
207 first = false;
208 }
209 auto_vec <const svalue *> keys (m_map.elements ());
210 for (map_t::iterator iter = m_map.begin ();
211 iter != m_map.end ();
212 ++iter)
213 keys.quick_push ((*iter).first);
214 keys.qsort (svalue::cmp_ptr_ptr)qsort (svalue::cmp_ptr_ptr);
215 unsigned i;
216 const svalue *sval;
217 FOR_EACH_VEC_ELT (keys, i, sval)for (i = 0; (keys).iterate ((i), &(sval)); ++(i))
218 {
219 if (multiline)
220 pp_string (pp, " ");
221 else if (!first)
222 pp_string (pp, ", ");
223 first = false;
224 if (!flag_dump_noaddrglobal_options.x_flag_dump_noaddr)
225 {
226 pp_pointer (pp, sval)do { sprintf ((pp)->buffer->digit_buffer, "%p", sval); pp_string
(pp, (pp)->buffer->digit_buffer); } while (0)
;
227 pp_string (pp, ": ");
228 }
229 sval->dump_to_pp (pp, simple);
230
231 entry_t e = *const_cast <map_t &> (m_map).get (sval);
232 pp_string (pp, ": ");
233 e.m_state->dump_to_pp (pp);
234 if (model)
235 if (tree rep = model->get_representative_tree (sval))
236 {
237 pp_string (pp, " (");
238 dump_quoted_tree (pp, rep);
239 pp_character (pp, ')');
240 }
241 if (e.m_origin)
242 {
243 pp_string (pp, " (origin: ");
244 if (!flag_dump_noaddrglobal_options.x_flag_dump_noaddr)
245 {
246 pp_pointer (pp, e.m_origin)do { sprintf ((pp)->buffer->digit_buffer, "%p", e.m_origin
); pp_string (pp, (pp)->buffer->digit_buffer); } while (
0)
;
247 pp_string (pp, ": ");
248 }
249 e.m_origin->dump_to_pp (pp, simple);
250 if (model)
251 if (tree rep = model->get_representative_tree (e.m_origin))
252 {
253 pp_string (pp, " (");
254 dump_quoted_tree (pp, rep);
255 pp_character (pp, ')');
256 }
257 pp_string (pp, ")");
258 }
259 if (multiline)
260 pp_newline (pp);
261 }
262 if (!multiline)
263 pp_string (pp, "}");
264}
265
266/* Dump this object to stderr. */
267
268DEBUG_FUNCTION__attribute__ ((__used__)) void
269sm_state_map::dump (bool simple) const
270{
271 pretty_printer pp;
272 pp_format_decoder (&pp)(&pp)->format_decoder = default_tree_printer;
273 pp_show_color (&pp)(&pp)->show_color = pp_show_color (global_dc->printer)(global_dc->printer)->show_color;
274 pp.buffer->stream = stderrstderr;
275 print (NULLnullptr, simple, true, &pp);
276 pp_newline (&pp);
277 pp_flush (&pp);
278}
279
280/* Return a new json::object of the form
281 {"global" : (optional) value for global state,
282 SVAL_DESC : value for state}. */
283
284json::object *
285sm_state_map::to_json () const
286{
287 json::object *map_obj = new json::object ();
288
289 if (m_global_state != m_sm.get_start_state ())
290 map_obj->set ("global", m_global_state->to_json ());
291 for (map_t::iterator iter = m_map.begin ();
292 iter != m_map.end ();
293 ++iter)
294 {
295 const svalue *sval = (*iter).first;
296 entry_t e = (*iter).second;
297
298 label_text sval_desc = sval->get_desc ();
299 map_obj->set (sval_desc.get (), e.m_state->to_json ());
300
301 /* This doesn't yet JSONify e.m_origin. */
302 }
303 return map_obj;
304}
305
306/* Return true if no states have been set within this map
307 (all expressions are for the start state). */
308
309bool
310sm_state_map::is_empty_p () const
311{
312 return m_map.elements () == 0 && m_global_state == m_sm.get_start_state ();
313}
314
315/* Generate a hash value for this sm_state_map. */
316
317hashval_t
318sm_state_map::hash () const
319{
320 hashval_t result = 0;
321
322 /* Accumulate the result by xoring a hash for each slot, so that the
323 result doesn't depend on the ordering of the slots in the map. */
324
325 for (map_t::iterator iter = m_map.begin ();
326 iter != m_map.end ();
327 ++iter)
328 {
329 inchash::hash hstate;
330 hstate.add_ptr ((*iter).first);
331 entry_t e = (*iter).second;
332 hstate.add_int (e.m_state->get_id ());
333 hstate.add_ptr (e.m_origin);
334 result ^= hstate.end ();
335 }
336 result ^= m_global_state->get_id ();
337
338 return result;
339}
340
341/* Equality operator for sm_state_map. */
342
343bool
344sm_state_map::operator== (const sm_state_map &other) const
345{
346 if (m_global_state != other.m_global_state)
347 return false;
348
349 if (m_map.elements () != other.m_map.elements ())
350 return false;
351
352 for (map_t::iterator iter = m_map.begin ();
353 iter != m_map.end ();
354 ++iter)
355 {
356 const svalue *sval = (*iter).first;
357 entry_t e = (*iter).second;
358 entry_t *other_slot = const_cast <map_t &> (other.m_map).get (sval);
359 if (other_slot == NULLnullptr)
360 return false;
361 if (e != *other_slot)
362 return false;
363 }
364
365 gcc_checking_assert (hash () == other.hash ())((void)(!(hash () == other.hash ()) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 365, __FUNCTION__), 0 : 0))
;
366
367 return true;
368}
369
370/* Get the state of SVAL within this object.
371 States default to the start state. */
372
373state_machine::state_t
374sm_state_map::get_state (const svalue *sval,
375 const extrinsic_state &ext_state) const
376{
377 gcc_assert (sval)((void)(!(sval) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 377, __FUNCTION__), 0 : 0))
;
378
379 sval = canonicalize_svalue (sval, ext_state);
380
381 if (entry_t *slot
382 = const_cast <map_t &> (m_map).get (sval))
383 return slot->m_state;
384
385 /* SVAL has no explicit sm-state.
386 If this sm allows for state inheritance, then SVAL might have implicit
387 sm-state inherited via a parent.
388 For example INIT_VAL(foo.field) might inherit taintedness state from
389 INIT_VAL(foo). */
390 if (m_sm.inherited_state_p ())
391 if (region_model_manager *mgr = ext_state.get_model_manager ())
392 {
393 if (const initial_svalue *init_sval = sval->dyn_cast_initial_svalue ())
394 {
395 const region *reg = init_sval->get_region ();
396 /* Try recursing upwards (up to the base region for the
397 cluster). */
398 if (!reg->base_region_p ())
399 if (const region *parent_reg = reg->get_parent_region ())
400 {
401 const svalue *parent_init_sval
402 = mgr->get_or_create_initial_value (parent_reg);
403 state_machine::state_t parent_state
404 = get_state (parent_init_sval, ext_state);
405 if (parent_state)
406 return parent_state;
407 }
408 }
409 else if (const sub_svalue *sub_sval = sval->dyn_cast_sub_svalue ())
410 {
411 const svalue *parent_sval = sub_sval->get_parent ();
412 if (state_machine::state_t parent_state
413 = get_state (parent_sval, ext_state))
414 return parent_state;
415 }
416 }
417
418 if (state_machine::state_t state
419 = m_sm.alt_get_inherited_state (*this, sval, ext_state))
420 return state;
421
422 return m_sm.get_default_state (sval);
423}
424
425/* Get the "origin" svalue for any state of SVAL. */
426
427const svalue *
428sm_state_map::get_origin (const svalue *sval,
429 const extrinsic_state &ext_state) const
430{
431 gcc_assert (sval)((void)(!(sval) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 431, __FUNCTION__), 0 : 0))
;
432
433 sval = canonicalize_svalue (sval, ext_state);
434
435 entry_t *slot
436 = const_cast <map_t &> (m_map).get (sval);
437 if (slot)
438 return slot->m_origin;
439 else
440 return NULLnullptr;
441}
442
443/* Set the state of SID within MODEL to STATE, recording that
444 the state came from ORIGIN. */
445
446void
447sm_state_map::set_state (region_model *model,
448 const svalue *sval,
449 state_machine::state_t state,
450 const svalue *origin,
451 const extrinsic_state &ext_state)
452{
453 if (model == NULLnullptr)
454 return;
455
456 /* Reject attempts to set state on UNKNOWN/POISONED. */
457 if (!sval->can_have_associated_state_p ())
458 return;
459
460 equiv_class &ec = model->get_constraints ()->get_equiv_class (sval);
461 if (!set_state (ec, state, origin, ext_state))
462 return;
463}
464
465/* Set the state of EC to STATE, recording that the state came from
466 ORIGIN.
467 Return true if any states of svalue_ids within EC changed. */
468
469bool
470sm_state_map::set_state (const equiv_class &ec,
471 state_machine::state_t state,
472 const svalue *origin,
473 const extrinsic_state &ext_state)
474{
475 bool any_changed = false;
476 for (const svalue *sval : ec.m_vars)
477 any_changed |= impl_set_state (sval, state, origin, ext_state);
478 return any_changed;
479}
480
481/* Set state of SVAL to STATE, bypassing equivalence classes.
482 Return true if the state changed. */
483
484bool
485sm_state_map::impl_set_state (const svalue *sval,
486 state_machine::state_t state,
487 const svalue *origin,
488 const extrinsic_state &ext_state)
489{
490 sval = canonicalize_svalue (sval, ext_state);
491
492 if (get_state (sval, ext_state) == state)
493 return false;
494
495 gcc_assert (sval->can_have_associated_state_p ())((void)(!(sval->can_have_associated_state_p ()) ? fancy_abort
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 495, __FUNCTION__), 0 : 0))
;
496
497 if (m_sm.inherited_state_p ())
498 {
499 if (const compound_svalue *compound_sval
500 = sval->dyn_cast_compound_svalue ())
501 for (auto iter : *compound_sval)
502 {
503 const svalue *inner_sval = iter.second;
504 if (inner_sval->can_have_associated_state_p ())
505 impl_set_state (inner_sval, state, origin, ext_state);
506 }
507 }
508
509 /* Special-case state 0 as the default value. */
510 if (state == 0)
511 {
512 if (m_map.get (sval))
513 m_map.remove (sval);
514 return true;
515 }
516 gcc_assert (sval)((void)(!(sval) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 516, __FUNCTION__), 0 : 0))
;
517 m_map.put (sval, entry_t (state, origin));
518 return true;
519}
520
521/* Clear any state for SVAL from this state map. */
522
523void
524sm_state_map::clear_any_state (const svalue *sval)
525{
526 m_map.remove (sval);
527}
528
529/* Set the "global" state within this state map to STATE. */
530
531void
532sm_state_map::set_global_state (state_machine::state_t state)
533{
534 m_global_state = state;
535}
536
537/* Get the "global" state within this state map. */
538
539state_machine::state_t
540sm_state_map::get_global_state () const
541{
542 return m_global_state;
543}
544
545/* Purge any state for SVAL.
546 If !SM::can_purge_p, then report the state as leaking,
547 using CTXT. */
548
549void
550sm_state_map::on_svalue_leak (const svalue *sval,
551 impl_region_model_context *ctxt)
552{
553 if (state_machine::state_t state = get_state (sval, ctxt->m_ext_state))
554 {
555 if (!m_sm.can_purge_p (state))
556 ctxt->on_state_leak (m_sm, sval, state);
557 m_map.remove (sval);
558 }
559}
560
561/* Purge any state for svalues that aren't live with respect to LIVE_SVALUES
562 and MODEL. */
563
564void
565sm_state_map::on_liveness_change (const svalue_set &live_svalues,
566 const region_model *model,
567 impl_region_model_context *ctxt)
568{
569 svalue_set svals_to_unset;
570 uncertainty_t *uncertainty = ctxt->get_uncertainty ();
571
572 auto_vec<const svalue *> leaked_svals (m_map.elements ());
573 for (map_t::iterator iter = m_map.begin ();
574 iter != m_map.end ();
575 ++iter)
576 {
577 const svalue *iter_sval = (*iter).first;
578 if (!iter_sval->live_p (&live_svalues, model))
579 {
580 svals_to_unset.add (iter_sval);
581 entry_t e = (*iter).second;
582 if (!m_sm.can_purge_p (e.m_state))
583 leaked_svals.quick_push (iter_sval);
584 }
585 if (uncertainty)
586 if (uncertainty->unknown_sm_state_p (iter_sval))
587 svals_to_unset.add (iter_sval);
588 }
589
590 leaked_svals.qsort (svalue::cmp_ptr_ptr)qsort (svalue::cmp_ptr_ptr);
591
592 unsigned i;
593 const svalue *sval;
594 FOR_EACH_VEC_ELT (leaked_svals, i, sval)for (i = 0; (leaked_svals).iterate ((i), &(sval)); ++(i))
595 {
596 entry_t e = *m_map.get (sval);
597 ctxt->on_state_leak (m_sm, sval, e.m_state);
598 }
599
600 for (svalue_set::iterator iter = svals_to_unset.begin ();
601 iter != svals_to_unset.end (); ++iter)
602 m_map.remove (*iter);
603}
604
605/* Purge state from SVAL (in response to a call to an unknown function). */
606
607void
608sm_state_map::on_unknown_change (const svalue *sval,
609 bool is_mutable,
610 const extrinsic_state &ext_state)
611{
612 svalue_set svals_to_unset;
613
614 for (map_t::iterator iter = m_map.begin ();
615 iter != m_map.end ();
616 ++iter)
617 {
618 const svalue *key = (*iter).first;
619 entry_t e = (*iter).second;
620 /* We only want to purge state for some states when things
621 are mutable. For example, in sm-malloc.cc, an on-stack ptr
622 doesn't stop being stack-allocated when passed to an unknown fn. */
623 if (!m_sm.reset_when_passed_to_unknown_fn_p (e.m_state, is_mutable))
624 continue;
625 if (key == sval)
626 svals_to_unset.add (key);
627 /* If we have INIT_VAL(BASE_REG), then unset any INIT_VAL(REG)
628 for REG within BASE_REG. */
629 if (const initial_svalue *init_sval = sval->dyn_cast_initial_svalue ())
630 if (const initial_svalue *init_key = key->dyn_cast_initial_svalue ())
631 {
632 const region *changed_reg = init_sval->get_region ();
633 const region *changed_key = init_key->get_region ();
634 if (changed_key->get_base_region () == changed_reg)
635 svals_to_unset.add (key);
636 }
637 }
638
639 for (svalue_set::iterator iter = svals_to_unset.begin ();
640 iter != svals_to_unset.end (); ++iter)
641 impl_set_state (*iter, (state_machine::state_t)0, NULLnullptr, ext_state);
642}
643
644/* Purge state for things involving SVAL.
645 For use when SVAL changes meaning, at the def_stmt on an SSA_NAME. */
646
647void
648sm_state_map::purge_state_involving (const svalue *sval,
649 const extrinsic_state &ext_state)
650{
651 /* Currently svalue::involves_p requires this. */
652 if (!(sval->get_kind () == SK_INITIAL
653 || sval->get_kind () == SK_CONJURED))
654 return;
655
656 svalue_set svals_to_unset;
657
658 for (map_t::iterator iter = m_map.begin ();
659 iter != m_map.end ();
660 ++iter)
661 {
662 const svalue *key = (*iter).first;
663 entry_t e = (*iter).second;
664 if (!m_sm.can_purge_p (e.m_state))
665 continue;
666 if (key->involves_p (sval))
667 svals_to_unset.add (key);
668 }
669
670 for (svalue_set::iterator iter = svals_to_unset.begin ();
671 iter != svals_to_unset.end (); ++iter)
672 impl_set_state (*iter, (state_machine::state_t)0, NULLnullptr, ext_state);
673}
674
675/* Comparator for imposing an order on sm_state_map instances. */
676
677int
678sm_state_map::cmp (const sm_state_map &smap_a, const sm_state_map &smap_b)
679{
680 if (int cmp_count = smap_a.elements () - smap_b.elements ())
681 return cmp_count;
682
683 auto_vec <const svalue *> keys_a (smap_a.elements ());
684 for (map_t::iterator iter = smap_a.begin ();
685 iter != smap_a.end ();
686 ++iter)
687 keys_a.quick_push ((*iter).first);
688 keys_a.qsort (svalue::cmp_ptr_ptr)qsort (svalue::cmp_ptr_ptr);
689
690 auto_vec <const svalue *> keys_b (smap_b.elements ());
691 for (map_t::iterator iter = smap_b.begin ();
692 iter != smap_b.end ();
693 ++iter)
694 keys_b.quick_push ((*iter).first);
695 keys_b.qsort (svalue::cmp_ptr_ptr)qsort (svalue::cmp_ptr_ptr);
696
697 unsigned i;
698 const svalue *sval_a;
699 FOR_EACH_VEC_ELT (keys_a, i, sval_a)for (i = 0; (keys_a).iterate ((i), &(sval_a)); ++(i))
700 {
701 const svalue *sval_b = keys_b[i];
702 if (int cmp_sval = svalue::cmp_ptr (sval_a, sval_b))
703 return cmp_sval;
704 const entry_t *e_a = const_cast <map_t &> (smap_a.m_map).get (sval_a);
705 const entry_t *e_b = const_cast <map_t &> (smap_b.m_map).get (sval_b);
706 if (int cmp_entry = entry_t::cmp (*e_a, *e_b))
707 return cmp_entry;
708 }
709
710 return 0;
711}
712
713/* Canonicalize SVAL before getting/setting it within the map.
714 Convert all NULL pointers to (void *) to avoid state explosions
715 involving all of the various (foo *)NULL vs (bar *)NULL. */
716
717const svalue *
718sm_state_map::canonicalize_svalue (const svalue *sval,
719 const extrinsic_state &ext_state)
720{
721 region_model_manager *mgr = ext_state.get_model_manager ();
722 if (mgr && sval->get_type () && POINTER_TYPE_P (sval->get_type ())(((enum tree_code) (sval->get_type ())->base.code) == POINTER_TYPE
|| ((enum tree_code) (sval->get_type ())->base.code) ==
REFERENCE_TYPE)
)
723 if (tree cst = sval->maybe_get_constant ())
724 if (zerop (cst))
725 return mgr->get_or_create_constant_svalue (null_pointer_nodeglobal_trees[TI_NULL_POINTER]);
726
727 return sval;
728}
729
730/* Attempt to merge this state map with OTHER, writing the result
731 into *OUT.
732 Return true if the merger was possible, false otherwise.
733
734 Normally, only identical state maps can be merged, so that
735 differences between state maps lead to different enodes
736
737 However some state machines may support merging states to
738 allow for discarding of less important states, and thus avoid
739 blow-up of the exploded graph. */
740
741bool
742sm_state_map::can_merge_with_p (const sm_state_map &other,
743 const state_machine &sm,
744 const extrinsic_state &ext_state,
745 sm_state_map **out) const
746{
747 /* If identical, then they merge trivially, with a copy. */
748 if (*this == other)
749 {
750 delete *out;
751 *out = clone ();
752 return true;
753 }
754
755 delete *out;
756 *out = new sm_state_map (sm);
757
758 /* Otherwise, attempt to merge element by element. */
759
760 /* Try to merge global state. */
761 if (state_machine::state_t merged_global_state
762 = sm.maybe_get_merged_state (get_global_state (),
763 other.get_global_state ()))
764 (*out)->set_global_state (merged_global_state);
765 else
766 return false;
767
768 /* Try to merge state each svalue's state (for the union
769 of svalues represented by each smap).
770 Ignore the origin information. */
771 hash_set<const svalue *> svals;
772 for (auto kv : *this)
773 svals.add (kv.first);
774 for (auto kv : other)
775 svals.add (kv.first);
776 for (auto sval : svals)
777 {
778 state_machine::state_t this_state = get_state (sval, ext_state);
779 state_machine::state_t other_state = other.get_state (sval, ext_state);
780 if (state_machine::state_t merged_state
781 = sm.maybe_get_merged_state (this_state, other_state))
782 (*out)->impl_set_state (sval, merged_state, NULLnullptr, ext_state);
783 else
784 return false;
785 }
786
787 /* Successfully merged all elements. */
788 return true;
789}
790
791/* class program_state. */
792
793/* program_state's ctor. */
794
795program_state::program_state (const extrinsic_state &ext_state)
796: m_region_model (NULLnullptr),
797 m_checker_states (ext_state.get_num_checkers ()),
798 m_valid (true)
799{
800 engine *eng = ext_state.get_engine ();
801 region_model_manager *mgr = eng->get_model_manager ();
802 m_region_model = new region_model (mgr);
803 const int num_states = ext_state.get_num_checkers ();
804 for (int i = 0; i < num_states; i++)
805 {
806 sm_state_map *sm = new sm_state_map (ext_state.get_sm (i));
807 m_checker_states.quick_push (sm);
808 }
809}
810
811/* Attempt to to use R to replay SUMMARY into this object.
812 Return true if it is possible. */
813
814bool
815sm_state_map::replay_call_summary (call_summary_replay &r,
816 const sm_state_map &summary)
817{
818 for (auto kv : summary.m_map)
819 {
820 const svalue *summary_sval = kv.first;
821 const svalue *caller_sval = r.convert_svalue_from_summary (summary_sval);
822 if (!caller_sval)
823 continue;
824 if (!caller_sval->can_have_associated_state_p ())
825 continue;
826 const svalue *summary_origin = kv.second.m_origin;
827 const svalue *caller_origin
828 = (summary_origin
829 ? r.convert_svalue_from_summary (summary_origin)
830 : NULLnullptr);
831 // caller_origin can be NULL.
832 m_map.put (caller_sval, entry_t (kv.second.m_state, caller_origin));
833 }
834 m_global_state = summary.m_global_state;
835 return true;
836}
837
838/* program_state's copy ctor. */
839
840program_state::program_state (const program_state &other)
841: m_region_model (new region_model (*other.m_region_model)),
842 m_checker_states (other.m_checker_states.length ()),
843 m_valid (true)
844{
845 int i;
846 sm_state_map *smap;
847 FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)for (i = 0; (other.m_checker_states).iterate ((i), &(smap
)); ++(i))
848 m_checker_states.quick_push (smap->clone ());
849}
850
851/* program_state's assignment operator. */
852
853program_state&
1
Assuming other == *this
854program_state::operator= (const program_state &other)
855{
856 delete m_region_model;
2
Memory is released
857 m_region_model = new region_model (*other.m_region_model);
3
Use of memory after it is freed
858
859 int i;
860 sm_state_map *smap;
861 FOR_EACH_VEC_ELT (m_checker_states, i, smap)for (i = 0; (m_checker_states).iterate ((i), &(smap)); ++
(i))
862 delete smap;
863 m_checker_states.truncate (0);
864 gcc_assert (m_checker_states.space (other.m_checker_states.length ()))((void)(!(m_checker_states.space (other.m_checker_states.length
())) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 864, __FUNCTION__), 0 : 0))
;
865
866 FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)for (i = 0; (other.m_checker_states).iterate ((i), &(smap
)); ++(i))
867 m_checker_states.quick_push (smap->clone ());
868
869 m_valid = other.m_valid;
870
871 return *this;
872}
873
874/* Move constructor for program_state (when building with C++11). */
875program_state::program_state (program_state &&other)
876: m_region_model (other.m_region_model),
877 m_checker_states (other.m_checker_states.length ())
878{
879 other.m_region_model = NULLnullptr;
880
881 int i;
882 sm_state_map *smap;
883 FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)for (i = 0; (other.m_checker_states).iterate ((i), &(smap
)); ++(i))
884 m_checker_states.quick_push (smap);
885 other.m_checker_states.truncate (0);
886
887 m_valid = other.m_valid;
888}
889
890/* program_state's dtor. */
891
892program_state::~program_state ()
893{
894 delete m_region_model;
895}
896
897/* Generate a hash value for this program_state. */
898
899hashval_t
900program_state::hash () const
901{
902 hashval_t result = m_region_model->hash ();
903
904 int i;
905 sm_state_map *smap;
906 FOR_EACH_VEC_ELT (m_checker_states, i, smap)for (i = 0; (m_checker_states).iterate ((i), &(smap)); ++
(i))
907 result ^= smap->hash ();
908 return result;
909}
910
911/* Equality operator for program_state.
912 All parts of the program_state (region model, checker states) must
913 equal their counterparts in OTHER for the two program_states to be
914 considered equal. */
915
916bool
917program_state::operator== (const program_state &other) const
918{
919 if (!(*m_region_model == *other.m_region_model))
920 return false;
921
922 int i;
923 sm_state_map *smap;
924 FOR_EACH_VEC_ELT (m_checker_states, i, smap)for (i = 0; (m_checker_states).iterate ((i), &(smap)); ++
(i))
925 if (!(*smap == *other.m_checker_states[i]))
926 return false;
927
928 gcc_checking_assert (hash () == other.hash ())((void)(!(hash () == other.hash ()) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 928, __FUNCTION__), 0 : 0))
;
929
930 return true;
931}
932
933/* Print a compact representation of this state to PP. */
934
935void
936program_state::print (const extrinsic_state &ext_state,
937 pretty_printer *pp) const
938{
939 pp_printf (pp, "rmodel: ");
940 m_region_model->dump_to_pp (pp, true, false);
941 pp_newline (pp);
942
943 int i;
944 sm_state_map *smap;
945 FOR_EACH_VEC_ELT (m_checker_states, i, smap)for (i = 0; (m_checker_states).iterate ((i), &(smap)); ++
(i))
946 {
947 if (!smap->is_empty_p ())
948 {
949 pp_printf (pp, "%s: ", ext_state.get_name (i));
950 smap->print (m_region_model, true, false, pp);
951 pp_newline (pp);
952 }
953 }
954 if (!m_valid)
955 {
956 pp_printf (pp, "invalid state");
957 pp_newline (pp);
958 }
959}
960
961/* Dump a representation of this state to PP. */
962
963void
964program_state::dump_to_pp (const extrinsic_state &ext_state,
965 bool /*summarize*/, bool multiline,
966 pretty_printer *pp) const
967{
968 if (!multiline)
969 pp_string (pp, "{");
970 {
971 pp_printf (pp, "rmodel:");
972 if (multiline)
973 pp_newline (pp);
974 else
975 pp_string (pp, " {");
976 m_region_model->dump_to_pp (pp, true, multiline);
977 if (!multiline)
978 pp_string (pp, "}");
979 }
980
981 int i;
982 sm_state_map *smap;
983 FOR_EACH_VEC_ELT (m_checker_states, i, smap)for (i = 0; (m_checker_states).iterate ((i), &(smap)); ++
(i))
984 {
985 if (!smap->is_empty_p ())
986 {
987 if (!multiline)
988 pp_string (pp, " {");
989 pp_printf (pp, "%s: ", ext_state.get_name (i));
990 if (multiline)
991 pp_newline (pp);
992 smap->print (m_region_model, true, multiline, pp);
993 if (!multiline)
994 pp_string (pp, "}");
995 }
996 }
997
998 if (!m_valid)
999 {
1000 if (!multiline)
1001 pp_space (pp)pp_character (pp, ' ');
1002 pp_printf (pp, "invalid state");
1003 if (multiline)
1004 pp_newline (pp);
1005 }
1006 if (!multiline)
1007 pp_string (pp, "}");
1008}
1009
1010/* Dump a representation of this state to OUTF. */
1011
1012void
1013program_state::dump_to_file (const extrinsic_state &ext_state,
1014 bool summarize, bool multiline,
1015 FILE *outf) const
1016{
1017 pretty_printer pp;
1018 pp_format_decoder (&pp)(&pp)->format_decoder = default_tree_printer;
1019 if (outf == stderrstderr)
1020 pp_show_color (&pp)(&pp)->show_color = pp_show_color (global_dc->printer)(global_dc->printer)->show_color;
1021 pp.buffer->stream = outf;
1022 dump_to_pp (ext_state, summarize, multiline, &pp);
1023 pp_flush (&pp);
1024}
1025
1026/* Dump a multiline representation of this state to stderr. */
1027
1028DEBUG_FUNCTION__attribute__ ((__used__)) void
1029program_state::dump (const extrinsic_state &ext_state,
1030 bool summarize) const
1031{
1032 dump_to_file (ext_state, summarize, true, stderrstderr);
1033}
1034
1035/* Return a new json::object of the form
1036 {"store" : object for store,
1037 "constraints" : object for constraint_manager,
1038 "curr_frame" : (optional) str for current frame,
1039 "checkers" : { STATE_NAME : object per sm_state_map },
1040 "valid" : true/false}. */
1041
1042json::object *
1043program_state::to_json (const extrinsic_state &ext_state) const
1044{
1045 json::object *state_obj = new json::object ();
1046
1047 state_obj->set ("store", m_region_model->get_store ()->to_json ());
1048 state_obj->set ("constraints",
1049 m_region_model->get_constraints ()->to_json ());
1050 if (m_region_model->get_current_frame ())
1051 state_obj->set ("curr_frame",
1052 m_region_model->get_current_frame ()->to_json ());
1053
1054 /* Provide m_checker_states as an object, using names as keys. */
1055 {
1056 json::object *checkers_obj = new json::object ();
1057
1058 int i;
1059 sm_state_map *smap;
1060 FOR_EACH_VEC_ELT (m_checker_states, i, smap)for (i = 0; (m_checker_states).iterate ((i), &(smap)); ++
(i))
1061 if (!smap->is_empty_p ())
1062 checkers_obj->set (ext_state.get_name (i), smap->to_json ());
1063
1064 state_obj->set ("checkers", checkers_obj);
1065 }
1066
1067 state_obj->set ("valid", new json::literal (m_valid));
1068
1069 return state_obj;
1070}
1071
1072/* Update this program_state to reflect a top-level call to FUN.
1073 The params will have initial_svalues. */
1074
1075void
1076program_state::push_frame (const extrinsic_state &ext_state ATTRIBUTE_UNUSED__attribute__ ((__unused__)),
1077 function *fun)
1078{
1079 m_region_model->push_frame (fun, NULLnullptr, NULLnullptr);
1080}
1081
1082/* Get the current function of this state. */
1083
1084function *
1085program_state::get_current_function () const
1086{
1087 return m_region_model->get_current_function ();
1088}
1089
1090/* Determine if following edge SUCC from ENODE is valid within the graph EG
1091 and update this state accordingly in-place.
1092
1093 Return true if the edge can be followed, or false otherwise.
1094
1095 Check for relevant conditionals and switch-values for conditionals
1096 and switch statements, adding the relevant conditions to this state.
1097 Push/pop frames for interprocedural edges and update params/returned
1098 values.
1099
1100 This is the "state" half of exploded_node::on_edge. */
1101
1102bool
1103program_state::on_edge (exploded_graph &eg,
1104 exploded_node *enode,
1105 const superedge *succ,
1106 uncertainty_t *uncertainty)
1107{
1108 class my_path_context : public path_context
1109 {
1110 public:
1111 my_path_context (bool &terminated) : m_terminated (terminated) {}
1112 void bifurcate (std::unique_ptr<custom_edge_info>) final override
1113 {
1114 gcc_unreachable ()(fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1114, __FUNCTION__))
;
1115 }
1116
1117 void terminate_path () final override
1118 {
1119 m_terminated = true;
1120 }
1121
1122 bool terminate_path_p () const final override
1123 {
1124 return m_terminated;
1125 }
1126 bool &m_terminated;
1127 };
1128
1129 /* Update state. */
1130 const program_point &point = enode->get_point ();
1131 const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
1132
1133 /* For conditionals and switch statements, add the
1134 relevant conditions (for the specific edge) to new_state;
1135 skip edges for which the resulting constraints
1136 are impossible.
1137 This also updates frame information for call/return superedges.
1138 Adding the relevant conditions for the edge could also trigger
1139 sm-state transitions (e.g. transitions due to ptrs becoming known
1140 to be NULL or non-NULL) */
1141 bool terminated = false;
1142 my_path_context path_ctxt (terminated);
1143 impl_region_model_context ctxt (eg, enode,
1144 &enode->get_state (),
1145 this,
1146 uncertainty, &path_ctxt,
1147 last_stmt);
1148 if (!m_region_model->maybe_update_for_edge (*succ,
1149 last_stmt,
1150 &ctxt, NULLnullptr))
1151 {
1152 logger * const logger = eg.get_logger ();
1153 if (logger)
1154 logger->log ("edge to SN: %i is impossible"
1155 " due to region_model constraints",
1156 succ->m_dest->m_index);
1157 return false;
1158 }
1159 if (terminated)
1160 return false;
1161
1162 program_state::detect_leaks (enode->get_state (), *this,
1163 NULLnullptr, eg.get_ext_state (),
1164 &ctxt);
1165
1166 return true;
1167}
1168
1169/* Update this program_state to reflect a call to function
1170 represented by CALL_STMT.
1171 currently used only when the call doesn't have a superedge representing
1172 the call ( like call via a function pointer ) */
1173void
1174program_state::push_call (exploded_graph &eg,
1175 exploded_node *enode,
1176 const gcall *call_stmt,
1177 uncertainty_t *uncertainty)
1178{
1179 /* Update state. */
1180 const program_point &point = enode->get_point ();
1181 const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
1182
1183 impl_region_model_context ctxt (eg, enode,
1184 &enode->get_state (),
1185 this,
1186 uncertainty,
1187 NULLnullptr,
1188 last_stmt);
1189 m_region_model->update_for_gcall (call_stmt, &ctxt);
1190}
1191
1192/* Update this program_state to reflect a return from function
1193 call to which is represented by CALL_STMT.
1194 currently used only when the call doesn't have a superedge representing
1195 the return */
1196void
1197program_state::returning_call (exploded_graph &eg,
1198 exploded_node *enode,
1199 const gcall *call_stmt,
1200 uncertainty_t *uncertainty)
1201{
1202 /* Update state. */
1203 const program_point &point = enode->get_point ();
1204 const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
1205
1206 impl_region_model_context ctxt (eg, enode,
1207 &enode->get_state (),
1208 this,
1209 uncertainty,
1210 NULLnullptr,
1211 last_stmt);
1212 m_region_model->update_for_return_gcall (call_stmt, &ctxt);
1213}
1214
1215/* Generate a simpler version of THIS, discarding state that's no longer
1216 relevant at POINT.
1217 The idea is that we're more likely to be able to consolidate
1218 multiple (point, state) into single exploded_nodes if we discard
1219 irrelevant state (e.g. at the end of functions). */
1220
1221program_state
1222program_state::prune_for_point (exploded_graph &eg,
1223 const program_point &point,
1224 exploded_node *enode_for_diag,
1225 uncertainty_t *uncertainty) const
1226{
1227 logger * const logger = eg.get_logger ();
1228 LOG_SCOPE (logger)log_scope s (logger, __PRETTY_FUNCTION__);
1229
1230 function *fun = point.get_function ();
1231 if (!fun)
1232 return *this;
1233
1234 program_state new_state (*this);
1235
1236 const state_purge_map *pm = eg.get_purge_map ();
1237 if (pm)
1238 {
1239 unsigned num_ssas_purged = 0;
1240 unsigned num_decls_purged = 0;
1241 auto_vec<const decl_region *> regs;
1242 new_state.m_region_model->get_regions_for_current_frame (&regs);
1243 regs.qsort (region::cmp_ptr_ptr)qsort (region::cmp_ptr_ptr);
1244 unsigned i;
1245 const decl_region *reg;
1246 FOR_EACH_VEC_ELT (regs, i, reg)for (i = 0; (regs).iterate ((i), &(reg)); ++(i))
1247 {
1248 const tree node = reg->get_decl ();
1249 if (TREE_CODE (node)((enum tree_code) (node)->base.code) == SSA_NAME)
1250 {
1251 const tree ssa_name = node;
1252 const state_purge_per_ssa_name &per_ssa
1253 = pm->get_data_for_ssa_name (node);
1254 if (!per_ssa.needed_at_point_p (point.get_function_point ()))
1255 {
1256 /* Don't purge bindings of SSA names to svalues
1257 that have unpurgable sm-state, so that leaks are
1258 reported at the end of the function, rather than
1259 at the last place that such an SSA name is referred to.
1260
1261 But do purge them for temporaries (when SSA_NAME_VAR is
1262 NULL), so that we report for cases where a leak happens when
1263 a variable is overwritten with another value, so that the leak
1264 is reported at the point of overwrite, rather than having
1265 temporaries keep the value reachable until the frame is
1266 popped. */
1267 const svalue *sval
1268 = new_state.m_region_model->get_store_value (reg, NULLnullptr);
1269 if (!new_state.can_purge_p (eg.get_ext_state (), sval)
1270 && SSA_NAME_VAR (ssa_name)((tree_check ((ssa_name), "/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1270, __FUNCTION__, (SSA_NAME)))->ssa_name.var == (tree)
nullptr || ((enum tree_code) ((ssa_name)->ssa_name.var)->
base.code) == IDENTIFIER_NODE ? (tree) nullptr : (ssa_name)->
ssa_name.var)
)
1271 {
1272 /* (currently only state maps can keep things
1273 alive). */
1274 if (logger)
1275 logger->log ("not purging binding for %qE"
1276 " (used by state map)", ssa_name);
1277 continue;
1278 }
1279
1280 new_state.m_region_model->purge_region (reg);
1281 num_ssas_purged++;
1282 }
1283 }
1284 else
1285 {
1286 const tree decl = node;
1287 gcc_assert (TREE_CODE (node) == VAR_DECL((void)(!(((enum tree_code) (node)->base.code) == VAR_DECL
|| ((enum tree_code) (node)->base.code) == PARM_DECL || (
(enum tree_code) (node)->base.code) == RESULT_DECL) ? fancy_abort
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1289, __FUNCTION__), 0 : 0))
1288 || TREE_CODE (node) == PARM_DECL((void)(!(((enum tree_code) (node)->base.code) == VAR_DECL
|| ((enum tree_code) (node)->base.code) == PARM_DECL || (
(enum tree_code) (node)->base.code) == RESULT_DECL) ? fancy_abort
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1289, __FUNCTION__), 0 : 0))
1289 || TREE_CODE (node) == RESULT_DECL)((void)(!(((enum tree_code) (node)->base.code) == VAR_DECL
|| ((enum tree_code) (node)->base.code) == PARM_DECL || (
(enum tree_code) (node)->base.code) == RESULT_DECL) ? fancy_abort
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1289, __FUNCTION__), 0 : 0))
;
1290 if (const state_purge_per_decl *per_decl
1291 = pm->get_any_data_for_decl (decl))
1292 if (!per_decl->needed_at_point_p (point.get_function_point ()))
1293 {
1294 /* Don't purge bindings of decls if there are svalues
1295 that have unpurgable sm-state within the decl's cluster,
1296 so that leaks are reported at the end of the function,
1297 rather than at the last place that such a decl is
1298 referred to. */
1299 if (!new_state.can_purge_base_region_p (eg.get_ext_state (),
1300 reg))
1301 {
1302 /* (currently only state maps can keep things
1303 alive). */
1304 if (logger)
1305 logger->log ("not purging binding for %qE"
1306 " (value in binding used by state map)",
1307 decl);
1308 continue;
1309 }
1310
1311 new_state.m_region_model->purge_region (reg);
1312 num_decls_purged++;
1313 }
1314 }
1315 }
1316
1317 if (num_ssas_purged > 0 || num_decls_purged > 0)
1318 {
1319 if (logger)
1320 {
1321 logger->log ("num_ssas_purged: %i", num_ssas_purged);
1322 logger->log ("num_decl_purged: %i", num_decls_purged);
1323 }
1324 impl_region_model_context ctxt (eg, enode_for_diag,
1325 this,
1326 &new_state,
1327 uncertainty, NULLnullptr,
1328 point.get_stmt ());
1329 detect_leaks (*this, new_state, NULLnullptr, eg.get_ext_state (), &ctxt);
1330 }
1331 }
1332
1333 new_state.m_region_model->canonicalize ();
1334
1335 return new_state;
1336}
1337
1338/* Return true if there are no unpurgeable bindings within BASE_REG. */
1339
1340bool
1341program_state::can_purge_base_region_p (const extrinsic_state &ext_state,
1342 const region *base_reg) const
1343{
1344 binding_cluster *cluster
1345 = m_region_model->get_store ()->get_cluster (base_reg);
1346 if (!cluster)
1347 return true;
1348
1349 for (auto iter : *cluster)
1350 {
1351 const svalue *sval = iter.second;
1352 if (!can_purge_p (ext_state, sval))
1353 return false;
1354 }
1355
1356 return true;
1357}
1358
1359/* Get a representative tree to use for describing SVAL. */
1360
1361tree
1362program_state::get_representative_tree (const svalue *sval) const
1363{
1364 gcc_assert (m_region_model)((void)(!(m_region_model) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1364, __FUNCTION__), 0 : 0))
;
1365 return m_region_model->get_representative_tree (sval);
1366}
1367
1368/* Attempt to merge this state with OTHER, both at POINT.
1369 Write the result to *OUT.
1370 If the states were merged successfully, return true. */
1371
1372bool
1373program_state::can_merge_with_p (const program_state &other,
1374 const extrinsic_state &ext_state,
1375 const program_point &point,
1376 program_state *out) const
1377{
1378 gcc_assert (out)((void)(!(out) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1378, __FUNCTION__), 0 : 0))
;
1379 gcc_assert (m_region_model)((void)(!(m_region_model) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1379, __FUNCTION__), 0 : 0))
;
1380
1381 /* Attempt to merge the sm-states. */
1382 int i;
1383 sm_state_map *smap;
1384 FOR_EACH_VEC_ELT (out->m_checker_states, i, smap)for (i = 0; (out->m_checker_states).iterate ((i), &(smap
)); ++(i))
1385 if (!m_checker_states[i]->can_merge_with_p (*other.m_checker_states[i],
1386 ext_state.get_sm (i),
1387 ext_state,
1388 &out->m_checker_states[i]))
1389 return false;
1390
1391 /* Attempt to merge the region_models. */
1392 if (!m_region_model->can_merge_with_p (*other.m_region_model,
1393 point,
1394 out->m_region_model,
1395 &ext_state,
1396 this, &other))
1397 return false;
1398
1399 out->m_region_model->canonicalize ();
1400
1401 return true;
1402}
1403
1404/* Assert that this object is valid. */
1405
1406void
1407program_state::validate (const extrinsic_state &ext_state) const
1408{
1409 /* Skip this in a release build. */
1410#if !CHECKING_P1
1411 return;
1412#endif
1413
1414 gcc_assert (m_checker_states.length () == ext_state.get_num_checkers ())((void)(!(m_checker_states.length () == ext_state.get_num_checkers
()) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1414, __FUNCTION__), 0 : 0))
;
1415 m_region_model->validate ();
1416}
1417
1418static void
1419log_set_of_svalues (logger *logger, const char *name,
1420 const svalue_set &set)
1421{
1422 logger->log (name);
1423 logger->inc_indent ();
1424 auto_vec<const svalue *> sval_vecs (set.elements ());
1425 for (svalue_set::iterator iter = set.begin ();
1426 iter != set.end (); ++iter)
1427 sval_vecs.quick_push (*iter);
1428 sval_vecs.qsort (svalue::cmp_ptr_ptr)qsort (svalue::cmp_ptr_ptr);
1429 unsigned i;
1430 const svalue *sval;
1431 FOR_EACH_VEC_ELT (sval_vecs, i, sval)for (i = 0; (sval_vecs).iterate ((i), &(sval)); ++(i))
1432 {
1433 logger->start_log_line ();
1434 pretty_printer *pp = logger->get_printer ();
1435 if (!flag_dump_noaddrglobal_options.x_flag_dump_noaddr)
1436 {
1437 pp_pointer (pp, sval)do { sprintf ((pp)->buffer->digit_buffer, "%p", sval); pp_string
(pp, (pp)->buffer->digit_buffer); } while (0)
;
1438 pp_string (pp, ": ");
1439 }
1440 sval->dump_to_pp (pp, false);
1441 logger->end_log_line ();
1442 }
1443 logger->dec_indent ();
1444}
1445
1446/* Compare the sets of svalues reachable from each of SRC_STATE and DEST_STATE.
1447 For all svalues that are reachable in SRC_STATE and are not live in
1448 DEST_STATE (whether explicitly reachable in DEST_STATE, or implicitly live
1449 based on the former set), call CTXT->on_svalue_leak for them.
1450
1451 Call on_liveness_change on both the CTXT and on the DEST_STATE's
1452 constraint_manager, purging dead svalues from sm-state and from
1453 constraints, respectively.
1454
1455 This function should be called at each fine-grained state change, not
1456 just at exploded edges. */
1457
1458void
1459program_state::detect_leaks (const program_state &src_state,
1460 const program_state &dest_state,
1461 const svalue *extra_sval,
1462 const extrinsic_state &ext_state,
1463 region_model_context *ctxt)
1464{
1465 logger *logger = ext_state.get_logger ();
1466 LOG_SCOPE (logger)log_scope s (logger, __PRETTY_FUNCTION__);
1467 const uncertainty_t *uncertainty = ctxt->get_uncertainty ();
1468 if (logger)
1469 {
1470 pretty_printer *pp = logger->get_printer ();
1471 logger->start_log_line ();
1472 pp_string (pp, "src_state: ");
1473 src_state.dump_to_pp (ext_state, true, false, pp);
1474 logger->end_log_line ();
1475 logger->start_log_line ();
1476 pp_string (pp, "dest_state: ");
1477 dest_state.dump_to_pp (ext_state, true, false, pp);
1478 logger->end_log_line ();
1479 if (extra_sval)
1480 {
1481 logger->start_log_line ();
1482 pp_string (pp, "extra_sval: ");
1483 extra_sval->dump_to_pp (pp, true);
1484 logger->end_log_line ();
1485 }
1486 if (uncertainty)
1487 {
1488 logger->start_log_line ();
1489 pp_string (pp, "uncertainty: ");
1490 uncertainty->dump_to_pp (pp, true);
1491 logger->end_log_line ();
1492 }
1493 }
1494
1495 /* Get svalues reachable from each of src_state and dest_state.
1496 Get svalues *known* to be reachable in src_state.
1497 Pass in uncertainty for dest_state so that we additionally get svalues that
1498 *might* still be reachable in dst_state. */
1499 svalue_set known_src_svalues;
1500 src_state.m_region_model->get_reachable_svalues (&known_src_svalues,
1501 NULLnullptr, NULLnullptr);
1502 svalue_set maybe_dest_svalues;
1503 dest_state.m_region_model->get_reachable_svalues (&maybe_dest_svalues,
1504 extra_sval, uncertainty);
1505
1506 if (logger)
1507 {
1508 log_set_of_svalues (logger, "src_state known reachable svalues:",
1509 known_src_svalues);
1510 log_set_of_svalues (logger, "dest_state maybe reachable svalues:",
1511 maybe_dest_svalues);
1512 }
1513
1514 auto_vec <const svalue *> dead_svals (known_src_svalues.elements ());
1515 for (svalue_set::iterator iter = known_src_svalues.begin ();
1516 iter != known_src_svalues.end (); ++iter)
1517 {
1518 const svalue *sval = (*iter);
1519 /* For each sval reachable from SRC_STATE, determine if it is
1520 live in DEST_STATE: either explicitly reachable, implicitly
1521 live based on the set of explicitly reachable svalues,
1522 or possibly reachable as recorded in uncertainty.
1523 Record those that have ceased to be live i.e. were known
1524 to be live, and are now not known to be even possibly-live. */
1525 if (!sval->live_p (&maybe_dest_svalues, dest_state.m_region_model))
1526 dead_svals.quick_push (sval);
1527 }
1528
1529 /* Call CTXT->on_svalue_leak on all svals in SRC_STATE that have ceased
1530 to be live, sorting them first to ensure deterministic behavior. */
1531 dead_svals.qsort (svalue::cmp_ptr_ptr)qsort (svalue::cmp_ptr_ptr);
1532 unsigned i;
1533 const svalue *sval;
1534 FOR_EACH_VEC_ELT (dead_svals, i, sval)for (i = 0; (dead_svals).iterate ((i), &(sval)); ++(i))
1535 ctxt->on_svalue_leak (sval);
1536
1537 /* Purge dead svals from sm-state. */
1538 ctxt->on_liveness_change (maybe_dest_svalues,
1539 dest_state.m_region_model);
1540
1541 /* Purge dead svals from constraints. */
1542 dest_state.m_region_model->get_constraints ()->on_liveness_change
1543 (maybe_dest_svalues, dest_state.m_region_model);
1544
1545 /* Purge dead heap-allocated regions from dynamic extents. */
1546 for (const svalue *sval : dead_svals)
1547 if (const region *reg = sval->maybe_get_region ())
1548 if (reg->get_kind () == RK_HEAP_ALLOCATED)
1549 dest_state.m_region_model->unset_dynamic_extents (reg);
1550}
1551
1552/* Attempt to to use R to replay SUMMARY into this object.
1553 Return true if it is possible. */
1554
1555bool
1556program_state::replay_call_summary (call_summary_replay &r,
1557 const program_state &summary)
1558{
1559 if (!m_region_model->replay_call_summary (r, *summary.m_region_model))
1560 return false;
1561
1562 for (unsigned sm_idx = 0; sm_idx < m_checker_states.length (); sm_idx++)
1563 {
1564 const sm_state_map *summary_sm_map = summary.m_checker_states[sm_idx];
1565 m_checker_states[sm_idx]->replay_call_summary (r, *summary_sm_map);
1566 }
1567
1568 if (!summary.m_valid)
1569 m_valid = false;
1570
1571 return true;
1572}
1573
1574/* Handle calls to "__analyzer_dump_state". */
1575
1576void
1577program_state::impl_call_analyzer_dump_state (const gcall *call,
1578 const extrinsic_state &ext_state,
1579 region_model_context *ctxt)
1580{
1581 call_details cd (call, m_region_model, ctxt);
1582 const char *sm_name = cd.get_arg_string_literal (0);
1583 if (!sm_name)
1584 {
1585 error_at (call->location, "cannot determine state machine");
1586 return;
1587 }
1588 unsigned sm_idx;
1589 if (!ext_state.get_sm_idx_by_name (sm_name, &sm_idx))
1590 {
1591 error_at (call->location, "unrecognized state machine %qs", sm_name);
1592 return;
1593 }
1594 const sm_state_map *smap = m_checker_states[sm_idx];
1595
1596 const svalue *sval = cd.get_arg_svalue (1);
1597
1598 /* Strip off cast to int (due to variadic args). */
1599 if (const svalue *cast = sval->maybe_undo_cast ())
1600 sval = cast;
1601
1602 state_machine::state_t state = smap->get_state (sval, ext_state);
1603 warning_at (call->location, 0, "state: %qs", state->get_name ());
1604}
1605
1606#if CHECKING_P1
1607
1608namespace selftest {
1609
1610/* Tests for sm_state_map. */
1611
1612static void
1613test_sm_state_map ()
1614{
1615 tree x = build_global_decl ("x", integer_type_nodeinteger_types[itk_int]);
1616 tree y = build_global_decl ("y", integer_type_nodeinteger_types[itk_int]);
1617 tree z = build_global_decl ("z", integer_type_nodeinteger_types[itk_int]);
1618
1619 state_machine *sm = make_malloc_state_machine (NULLnullptr);
1620 auto_delete_vec <state_machine> checkers;
1621 checkers.safe_push (sm);
1622 engine eng;
1623 extrinsic_state ext_state (checkers, &eng);
1624 state_machine::state_t start = sm->get_start_state ();
1625
1626 /* Test setting states on svalue_id instances directly. */
1627 {
1628 const state_machine::state test_state_42 ("test state 42", 42);
1629 const state_machine::state_t TEST_STATE_42 = &test_state_42;
1630 region_model_manager mgr;
1631 region_model model (&mgr);
1632 const svalue *x_sval = model.get_rvalue (x, NULLnullptr);
1633 const svalue *y_sval = model.get_rvalue (y, NULLnullptr);
1634 const svalue *z_sval = model.get_rvalue (z, NULLnullptr);
1635
1636 sm_state_map map (*sm);
1637 ASSERT_TRUE (map.is_empty_p ())do { const char *desc_ = "ASSERT_TRUE (" "(map.is_empty_p ())"
")"; bool actual_ = ((map.is_empty_p ())); if (actual_) ::selftest
::pass (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1637, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1637, __FUNCTION__))), desc_); } while (0)
;
1638 ASSERT_EQ (map.get_state (x_sval, ext_state), start)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (x_sval, ext_state))"
", " "(start)" ")"; if (((map.get_state (x_sval, ext_state))
) == ((start))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1638, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1638, __FUNCTION__)))), desc_); } while (0)
;
1639
1640 map.impl_set_state (x_sval, TEST_STATE_42, z_sval, ext_state);
1641 ASSERT_EQ (map.get_state (x_sval, ext_state), TEST_STATE_42)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (x_sval, ext_state))"
", " "(TEST_STATE_42)" ")"; if (((map.get_state (x_sval, ext_state
))) == ((TEST_STATE_42))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1641, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1641, __FUNCTION__)))), desc_); } while (0)
;
1642 ASSERT_EQ (map.get_origin (x_sval, ext_state), z_sval)do { const char *desc_ = "ASSERT_EQ (" "(map.get_origin (x_sval, ext_state))"
", " "(z_sval)" ")"; if (((map.get_origin (x_sval, ext_state
))) == ((z_sval))) ::selftest::pass ((((::selftest::location (
"/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1642, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1642, __FUNCTION__)))), desc_); } while (0)
;
1643 ASSERT_EQ (map.get_state (y_sval, ext_state), start)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (y_sval, ext_state))"
", " "(start)" ")"; if (((map.get_state (y_sval, ext_state))
) == ((start))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1643, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1643, __FUNCTION__)))), desc_); } while (0)
;
1644 ASSERT_FALSE (map.is_empty_p ())do { const char *desc_ = "ASSERT_FALSE (" "(map.is_empty_p ())"
")"; bool actual_ = ((map.is_empty_p ())); if (actual_) ::selftest
::fail (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1644, __FUNCTION__))), desc_); else ::selftest::pass (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1644, __FUNCTION__))), desc_); } while (0)
;
1645
1646 map.impl_set_state (y_sval, 0, z_sval, ext_state);
1647 ASSERT_EQ (map.get_state (y_sval, ext_state), start)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (y_sval, ext_state))"
", " "(start)" ")"; if (((map.get_state (y_sval, ext_state))
) == ((start))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1647, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1647, __FUNCTION__)))), desc_); } while (0)
;
1648
1649 map.impl_set_state (x_sval, 0, z_sval, ext_state);
1650 ASSERT_EQ (map.get_state (x_sval, ext_state), start)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (x_sval, ext_state))"
", " "(start)" ")"; if (((map.get_state (x_sval, ext_state))
) == ((start))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1650, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1650, __FUNCTION__)))), desc_); } while (0)
;
1651 ASSERT_TRUE (map.is_empty_p ())do { const char *desc_ = "ASSERT_TRUE (" "(map.is_empty_p ())"
")"; bool actual_ = ((map.is_empty_p ())); if (actual_) ::selftest
::pass (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1651, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1651, __FUNCTION__))), desc_); } while (0)
;
1652 }
1653
1654 const state_machine::state test_state_5 ("test state 5", 5);
1655 const state_machine::state_t TEST_STATE_5 = &test_state_5;
1656
1657 /* Test setting states via equivalence classes. */
1658 {
1659 region_model_manager mgr;
1660 region_model model (&mgr);
1661 const svalue *x_sval = model.get_rvalue (x, NULLnullptr);
1662 const svalue *y_sval = model.get_rvalue (y, NULLnullptr);
1663 const svalue *z_sval = model.get_rvalue (z, NULLnullptr);
1664
1665 sm_state_map map (*sm);
1666 ASSERT_TRUE (map.is_empty_p ())do { const char *desc_ = "ASSERT_TRUE (" "(map.is_empty_p ())"
")"; bool actual_ = ((map.is_empty_p ())); if (actual_) ::selftest
::pass (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1666, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1666, __FUNCTION__))), desc_); } while (0)
;
1667 ASSERT_EQ (map.get_state (x_sval, ext_state), start)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (x_sval, ext_state))"
", " "(start)" ")"; if (((map.get_state (x_sval, ext_state))
) == ((start))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1667, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1667, __FUNCTION__)))), desc_); } while (0)
;
1668 ASSERT_EQ (map.get_state (y_sval, ext_state), start)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (y_sval, ext_state))"
", " "(start)" ")"; if (((map.get_state (y_sval, ext_state))
) == ((start))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1668, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1668, __FUNCTION__)))), desc_); } while (0)
;
1669
1670 model.add_constraint (x, EQ_EXPR, y, NULLnullptr);
1671
1672 /* Setting x to a state should also update y, as they
1673 are in the same equivalence class. */
1674 map.set_state (&model, x_sval, TEST_STATE_5, z_sval, ext_state);
1675 ASSERT_EQ (map.get_state (x_sval, ext_state), TEST_STATE_5)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (x_sval, ext_state))"
", " "(TEST_STATE_5)" ")"; if (((map.get_state (x_sval, ext_state
))) == ((TEST_STATE_5))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1675, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1675, __FUNCTION__)))), desc_); } while (0)
;
1676 ASSERT_EQ (map.get_state (y_sval, ext_state), TEST_STATE_5)do { const char *desc_ = "ASSERT_EQ (" "(map.get_state (y_sval, ext_state))"
", " "(TEST_STATE_5)" ")"; if (((map.get_state (y_sval, ext_state
))) == ((TEST_STATE_5))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1676, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1676, __FUNCTION__)))), desc_); } while (0)
;
1677 ASSERT_EQ (map.get_origin (x_sval, ext_state), z_sval)do { const char *desc_ = "ASSERT_EQ (" "(map.get_origin (x_sval, ext_state))"
", " "(z_sval)" ")"; if (((map.get_origin (x_sval, ext_state
))) == ((z_sval))) ::selftest::pass ((((::selftest::location (
"/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1677, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1677, __FUNCTION__)))), desc_); } while (0)
;
1678 ASSERT_EQ (map.get_origin (y_sval, ext_state), z_sval)do { const char *desc_ = "ASSERT_EQ (" "(map.get_origin (y_sval, ext_state))"
", " "(z_sval)" ")"; if (((map.get_origin (y_sval, ext_state
))) == ((z_sval))) ::selftest::pass ((((::selftest::location (
"/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1678, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1678, __FUNCTION__)))), desc_); } while (0)
;
1679 }
1680
1681 /* Test equality and hashing. */
1682 {
1683 region_model_manager mgr;
1684 region_model model (&mgr);
1685 const svalue *y_sval = model.get_rvalue (y, NULLnullptr);
1686 const svalue *z_sval = model.get_rvalue (z, NULLnullptr);
1687
1688 sm_state_map map0 (*sm);
1689 sm_state_map map1 (*sm);
1690 sm_state_map map2 (*sm);
1691
1692 ASSERT_EQ (map0.hash (), map1.hash ())do { const char *desc_ = "ASSERT_EQ (" "(map0.hash ())" ", " "(map1.hash ())"
")"; if (((map0.hash ())) == ((map1.hash ()))) ::selftest::pass
((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1692, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1692, __FUNCTION__)))), desc_); } while (0)
;
1693 ASSERT_EQ (map0, map1)do { const char *desc_ = "ASSERT_EQ (" "(map0)" ", " "(map1)"
")"; if (((map0)) == ((map1))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1693, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1693, __FUNCTION__)))), desc_); } while (0)
;
1694
1695 map1.impl_set_state (y_sval, TEST_STATE_5, z_sval, ext_state);
1696 ASSERT_NE (map0.hash (), map1.hash ())do { const char *desc_ = "ASSERT_NE (" "map0.hash ()" ", " "map1.hash ()"
")"; if ((map0.hash ()) != (map1.hash ())) ::selftest::pass (
(::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1696, __FUNCTION__)), desc_); else ::selftest::fail ((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1696, __FUNCTION__)), desc_); } while (0)
;
1697 ASSERT_NE (map0, map1)do { const char *desc_ = "ASSERT_NE (" "map0" ", " "map1" ")"
; if ((map0) != (map1)) ::selftest::pass ((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1697, __FUNCTION__)), desc_); else ::selftest::fail ((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1697, __FUNCTION__)), desc_); } while (0)
;
1698
1699 /* Make the same change to map2. */
1700 map2.impl_set_state (y_sval, TEST_STATE_5, z_sval, ext_state);
1701 ASSERT_EQ (map1.hash (), map2.hash ())do { const char *desc_ = "ASSERT_EQ (" "(map1.hash ())" ", " "(map2.hash ())"
")"; if (((map1.hash ())) == ((map2.hash ()))) ::selftest::pass
((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1701, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1701, __FUNCTION__)))), desc_); } while (0)
;
1702 ASSERT_EQ (map1, map2)do { const char *desc_ = "ASSERT_EQ (" "(map1)" ", " "(map2)"
")"; if (((map1)) == ((map2))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1702, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1702, __FUNCTION__)))), desc_); } while (0)
;
1703 }
1704
1705 /* Equality and hashing shouldn't depend on ordering. */
1706 {
1707 const state_machine::state test_state_2 ("test state 2", 2);
1708 const state_machine::state_t TEST_STATE_2 = &test_state_2;
1709 const state_machine::state test_state_3 ("test state 3", 3);
1710 const state_machine::state_t TEST_STATE_3 = &test_state_3;
1711 sm_state_map map0 (*sm);
1712 sm_state_map map1 (*sm);
1713 sm_state_map map2 (*sm);
1714
1715 ASSERT_EQ (map0.hash (), map1.hash ())do { const char *desc_ = "ASSERT_EQ (" "(map0.hash ())" ", " "(map1.hash ())"
")"; if (((map0.hash ())) == ((map1.hash ()))) ::selftest::pass
((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1715, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1715, __FUNCTION__)))), desc_); } while (0)
;
1716 ASSERT_EQ (map0, map1)do { const char *desc_ = "ASSERT_EQ (" "(map0)" ", " "(map1)"
")"; if (((map0)) == ((map1))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1716, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1716, __FUNCTION__)))), desc_); } while (0)
;
1717
1718 region_model_manager mgr;
1719 region_model model (&mgr);
1720 const svalue *x_sval = model.get_rvalue (x, NULLnullptr);
1721 const svalue *y_sval = model.get_rvalue (y, NULLnullptr);
1722 const svalue *z_sval = model.get_rvalue (z, NULLnullptr);
1723
1724 map1.impl_set_state (x_sval, TEST_STATE_2, NULLnullptr, ext_state);
1725 map1.impl_set_state (y_sval, TEST_STATE_3, NULLnullptr, ext_state);
1726 map1.impl_set_state (z_sval, TEST_STATE_2, NULLnullptr, ext_state);
1727
1728 map2.impl_set_state (z_sval, TEST_STATE_2, NULLnullptr, ext_state);
1729 map2.impl_set_state (y_sval, TEST_STATE_3, NULLnullptr, ext_state);
1730 map2.impl_set_state (x_sval, TEST_STATE_2, NULLnullptr, ext_state);
1731
1732 ASSERT_EQ (map1.hash (), map2.hash ())do { const char *desc_ = "ASSERT_EQ (" "(map1.hash ())" ", " "(map2.hash ())"
")"; if (((map1.hash ())) == ((map2.hash ()))) ::selftest::pass
((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1732, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1732, __FUNCTION__)))), desc_); } while (0)
;
1733 ASSERT_EQ (map1, map2)do { const char *desc_ = "ASSERT_EQ (" "(map1)" ", " "(map2)"
")"; if (((map1)) == ((map2))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1733, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1733, __FUNCTION__)))), desc_); } while (0)
;
1734 }
1735
1736 // TODO: coverage for purging
1737}
1738
1739/* Check program_state works as expected. */
1740
1741static void
1742test_program_state_1 ()
1743{
1744 /* Create a program_state for a global ptr "p" that has
1745 malloc sm-state, pointing to a region on the heap. */
1746 tree p = build_global_decl ("p", ptr_type_nodeglobal_trees[TI_PTR_TYPE]);
1747
1748 state_machine *sm = make_malloc_state_machine (NULLnullptr);
1749 const state_machine::state_t UNCHECKED_STATE
1750 = sm->get_state_by_name ("unchecked");
1751 auto_delete_vec <state_machine> checkers;
1752 checkers.safe_push (sm);
1753
1754 engine eng;
1755 extrinsic_state ext_state (checkers, &eng);
1756 region_model_manager *mgr = eng.get_model_manager ();
1757 program_state s (ext_state);
1758 region_model *model = s.m_region_model;
1759 const svalue *size_in_bytes
1760 = mgr->get_or_create_unknown_svalue (size_type_nodeglobal_trees[TI_SIZE_TYPE]);
1761 const region *new_reg
1762 = model->get_or_create_region_for_heap_alloc (size_in_bytes, NULLnullptr);
1763 const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_nodeglobal_trees[TI_PTR_TYPE], new_reg);
1764 model->set_value (model->get_lvalue (p, NULLnullptr),
1765 ptr_sval, NULLnullptr);
1766 sm_state_map *smap = s.m_checker_states[0];
1767
1768 smap->impl_set_state (ptr_sval, UNCHECKED_STATE, NULLnullptr, ext_state);
1769 ASSERT_EQ (smap->get_state (ptr_sval, ext_state), UNCHECKED_STATE)do { const char *desc_ = "ASSERT_EQ (" "(smap->get_state (ptr_sval, ext_state))"
", " "(UNCHECKED_STATE)" ")"; if (((smap->get_state (ptr_sval
, ext_state))) == ((UNCHECKED_STATE))) ::selftest::pass ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1769, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1769, __FUNCTION__)))), desc_); } while (0)
;
1770}
1771
1772/* Check that program_state works for string literals. */
1773
1774static void
1775test_program_state_2 ()
1776{
1777 /* Create a program_state for a global ptr "p" that points to
1778 a string constant. */
1779 tree p = build_global_decl ("p", ptr_type_nodeglobal_trees[TI_PTR_TYPE]);
1780
1781 tree string_cst_ptr = build_string_literal (4, "foo");
1782
1783 auto_delete_vec <state_machine> checkers;
1784 engine eng;
1785 extrinsic_state ext_state (checkers, &eng);
1786
1787 program_state s (ext_state);
1788 region_model *model = s.m_region_model;
1789 const region *p_reg = model->get_lvalue (p, NULLnullptr);
1790 const svalue *str_sval = model->get_rvalue (string_cst_ptr, NULLnullptr);
1791 model->set_value (p_reg, str_sval, NULLnullptr);
1792}
1793
1794/* Verify that program_states with identical sm-state can be merged,
1795 and that the merged program_state preserves the sm-state. */
1796
1797static void
1798test_program_state_merging ()
1799{
1800 /* Create a program_state for a global ptr "p" that has
1801 malloc sm-state, pointing to a region on the heap. */
1802 tree p = build_global_decl ("p", ptr_type_nodeglobal_trees[TI_PTR_TYPE]);
1803
1804 engine eng;
1805 region_model_manager *mgr = eng.get_model_manager ();
1806 program_point point (program_point::origin (*mgr));
1807 auto_delete_vec <state_machine> checkers;
1808 checkers.safe_push (make_malloc_state_machine (NULLnullptr));
1809 extrinsic_state ext_state (checkers, &eng);
1810
1811 program_state s0 (ext_state);
1812 uncertainty_t uncertainty;
1813 impl_region_model_context ctxt (&s0, ext_state, &uncertainty);
1814
1815 region_model *model0 = s0.m_region_model;
1816 const svalue *size_in_bytes
1817 = mgr->get_or_create_unknown_svalue (size_type_nodeglobal_trees[TI_SIZE_TYPE]);
1818 const region *new_reg
1819 = model0->get_or_create_region_for_heap_alloc (size_in_bytes, NULLnullptr);
1820 const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_nodeglobal_trees[TI_PTR_TYPE], new_reg);
1821 model0->set_value (model0->get_lvalue (p, &ctxt),
1822 ptr_sval, &ctxt);
1823 sm_state_map *smap = s0.m_checker_states[0];
1824 const state_machine::state test_state ("test state", 0);
1825 const state_machine::state_t TEST_STATE = &test_state;
1826 smap->impl_set_state (ptr_sval, TEST_STATE, NULLnullptr, ext_state);
1827 ASSERT_EQ (smap->get_state (ptr_sval, ext_state), TEST_STATE)do { const char *desc_ = "ASSERT_EQ (" "(smap->get_state (ptr_sval, ext_state))"
", " "(TEST_STATE)" ")"; if (((smap->get_state (ptr_sval,
ext_state))) == ((TEST_STATE))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1827, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1827, __FUNCTION__)))), desc_); } while (0)
;
1828
1829 model0->canonicalize ();
1830
1831 /* Verify that canonicalization preserves sm-state. */
1832 ASSERT_EQ (smap->get_state (model0->get_rvalue (p, NULL), ext_state),do { const char *desc_ = "ASSERT_EQ (" "(smap->get_state (model0->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((smap->get_state (model0->
get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE))) ::selftest
::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1833, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1833, __FUNCTION__)))), desc_); } while (0)
1833 TEST_STATE)do { const char *desc_ = "ASSERT_EQ (" "(smap->get_state (model0->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((smap->get_state (model0->
get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE))) ::selftest
::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1833, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1833, __FUNCTION__)))), desc_); } while (0)
;
1834
1835 /* Make a copy of the program_state. */
1836 program_state s1 (s0);
1837 ASSERT_EQ (s0, s1)do { const char *desc_ = "ASSERT_EQ (" "(s0)" ", " "(s1)" ")"
; if (((s0)) == ((s1))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1837, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1837, __FUNCTION__)))), desc_); } while (0)
;
1838
1839 /* We have two identical states with "p" pointing to a heap region
1840 with the given sm-state.
1841 They ought to be mergeable, preserving the sm-state. */
1842 program_state merged (ext_state);
1843 ASSERT_TRUE (s0.can_merge_with_p (s1, ext_state, point, &merged))do { const char *desc_ = "ASSERT_TRUE (" "(s0.can_merge_with_p (s1, ext_state, point, &merged))"
")"; bool actual_ = ((s0.can_merge_with_p (s1, ext_state, point
, &merged))); if (actual_) ::selftest::pass (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1843, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1843, __FUNCTION__))), desc_); } while (0)
;
1844 merged.validate (ext_state);
1845
1846 /* Verify that the merged state has the sm-state for "p". */
1847 region_model *merged_model = merged.m_region_model;
1848 sm_state_map *merged_smap = merged.m_checker_states[0];
1849 ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL),do { const char *desc_ = "ASSERT_EQ (" "(merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((merged_smap->get_state (merged_model
->get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE)))
::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1851, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1851, __FUNCTION__)))), desc_); } while (0)
1850 ext_state),do { const char *desc_ = "ASSERT_EQ (" "(merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((merged_smap->get_state (merged_model
->get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE)))
::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1851, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1851, __FUNCTION__)))), desc_); } while (0)
1851 TEST_STATE)do { const char *desc_ = "ASSERT_EQ (" "(merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((merged_smap->get_state (merged_model
->get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE)))
::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1851, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1851, __FUNCTION__)))), desc_); } while (0)
;
1852
1853 /* Try canonicalizing. */
1854 merged.m_region_model->canonicalize ();
1855 merged.validate (ext_state);
1856
1857 /* Verify that the merged state still has the sm-state for "p". */
1858 ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL),do { const char *desc_ = "ASSERT_EQ (" "(merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((merged_smap->get_state (merged_model
->get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE)))
::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1860, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1860, __FUNCTION__)))), desc_); } while (0)
1859 ext_state),do { const char *desc_ = "ASSERT_EQ (" "(merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((merged_smap->get_state (merged_model
->get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE)))
::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1860, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1860, __FUNCTION__)))), desc_); } while (0)
1860 TEST_STATE)do { const char *desc_ = "ASSERT_EQ (" "(merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state))"
", " "(TEST_STATE)" ")"; if (((merged_smap->get_state (merged_model
->get_rvalue (p, nullptr), ext_state))) == ((TEST_STATE)))
::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1860, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1860, __FUNCTION__)))), desc_); } while (0)
;
1861
1862 /* After canonicalization, we ought to have equality with the inputs. */
1863 ASSERT_EQ (s0, merged)do { const char *desc_ = "ASSERT_EQ (" "(s0)" ", " "(merged)"
")"; if (((s0)) == ((merged))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1863, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1863, __FUNCTION__)))), desc_); } while (0)
;
1864}
1865
1866/* Verify that program_states with different global-state in an sm-state
1867 can't be merged. */
1868
1869static void
1870test_program_state_merging_2 ()
1871{
1872 engine eng;
1873 region_model_manager *mgr = eng.get_model_manager ();
1874 program_point point (program_point::origin (*mgr));
1875 auto_delete_vec <state_machine> checkers;
1876 checkers.safe_push (make_signal_state_machine (NULLnullptr));
1877 extrinsic_state ext_state (checkers, &eng);
1878
1879 const state_machine::state test_state_0 ("test state 0", 0);
1880 const state_machine::state test_state_1 ("test state 1", 1);
1881 const state_machine::state_t TEST_STATE_0 = &test_state_0;
1882 const state_machine::state_t TEST_STATE_1 = &test_state_1;
1883
1884 program_state s0 (ext_state);
1885 {
1886 sm_state_map *smap0 = s0.m_checker_states[0];
1887 smap0->set_global_state (TEST_STATE_0);
1888 ASSERT_EQ (smap0->get_global_state (), TEST_STATE_0)do { const char *desc_ = "ASSERT_EQ (" "(smap0->get_global_state ())"
", " "(TEST_STATE_0)" ")"; if (((smap0->get_global_state (
))) == ((TEST_STATE_0))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1888, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1888, __FUNCTION__)))), desc_); } while (0)
;
1889 }
1890
1891 program_state s1 (ext_state);
1892 {
1893 sm_state_map *smap1 = s1.m_checker_states[0];
1894 smap1->set_global_state (TEST_STATE_1);
1895 ASSERT_EQ (smap1->get_global_state (), TEST_STATE_1)do { const char *desc_ = "ASSERT_EQ (" "(smap1->get_global_state ())"
", " "(TEST_STATE_1)" ")"; if (((smap1->get_global_state (
))) == ((TEST_STATE_1))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1895, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1895, __FUNCTION__)))), desc_); } while (0)
;
1896 }
1897
1898 ASSERT_NE (s0, s1)do { const char *desc_ = "ASSERT_NE (" "s0" ", " "s1" ")"; if
((s0) != (s1)) ::selftest::pass ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1898, __FUNCTION__)), desc_); else ::selftest::fail ((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1898, __FUNCTION__)), desc_); } while (0)
;
1899
1900 /* They ought to not be mergeable. */
1901 program_state merged (ext_state);
1902 ASSERT_FALSE (s0.can_merge_with_p (s1, ext_state, point, &merged))do { const char *desc_ = "ASSERT_FALSE (" "(s0.can_merge_with_p (s1, ext_state, point, &merged))"
")"; bool actual_ = ((s0.can_merge_with_p (s1, ext_state, point
, &merged))); if (actual_) ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1902, __FUNCTION__))), desc_); else ::selftest::pass (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/analyzer/program-state.cc"
, 1902, __FUNCTION__))), desc_); } while (0)
;
1903}
1904
1905/* Run all of the selftests within this file. */
1906
1907void
1908analyzer_program_state_cc_tests ()
1909{
1910 test_sm_state_map ();
1911 test_program_state_1 ();
1912 test_program_state_2 ();
1913 test_program_state_merging ();
1914 test_program_state_merging_2 ();
1915}
1916
1917} // namespace selftest
1918
1919#endif /* CHECKING_P */
1920
1921} // namespace ana
1922
1923#endif /* #if ENABLE_ANALYZER */