]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/analyzer/varargs.cc
analyzer: fix ICE in binding_cluster ctor [PR106401]
[thirdparty/gcc.git] / gcc / analyzer / varargs.cc
CommitLineData
2402dc6b
DM
1/* Implementation of <stdarg.h> within analyzer.
2 Copyright (C) 2022 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#include "system.h"
23#include "coretypes.h"
24#include "tree.h"
25#include "function.h"
26#include "basic-block.h"
27#include "gimple.h"
28#include "diagnostic-path.h"
29#include "json.h"
30#include "analyzer/analyzer.h"
31#include "analyzer/analyzer-logging.h"
32#include "analyzer/sm.h"
33#include "analyzer/pending-diagnostic.h"
34#include "tristate.h"
35#include "selftest.h"
36#include "analyzer/call-string.h"
37#include "analyzer/program-point.h"
38#include "analyzer/store.h"
39#include "analyzer/region-model.h"
40#include "analyzer/program-state.h"
41#include "analyzer/checker-path.h"
42#include "digraph.h"
43#include "ordered-hash-map.h"
44#include "cfg.h"
45#include "gimple-iterator.h"
46#include "analyzer/supergraph.h"
47#include "alloc-pool.h"
48#include "fibonacci_heap.h"
49#include "shortest-paths.h"
50#include "sbitmap.h"
51#include "analyzer/diagnostic-manager.h"
52#include "analyzer/exploded-graph.h"
f443024b 53#include "diagnostic-metadata.h"
2402dc6b
DM
54
55#if ENABLE_ANALYZER
56
57namespace ana {
58
59/* Implementation of <stdarg.h> within analyzer.
60
61 Objectives:
62 - detection of interprocedural type errors involving va_arg
63 - tracking of symbolic values interprocedurally from variadic call
64 through to va_arg unpacking
65 - detection of missing va_end
66 - detection of va_arg outside of a va_start/va_end pair
67 - detection of uses of a va_list after the frame in containing the
68 va_start has returned
69
70 The analyzer runs *before* the "stdarg" and "lower_vaarg" gimple
71 passes, which have target-dependent effects.
72
73 This file implements a state machine on svalues for tracking when
74 va_start has been called, so that we can detect missing va_end,
75 and misplaced va_arg, etc.
76 To do this requires an svalue that can have state, so we implement va_start
77 by creating a stack-allocated region, and use a pointer to that region
78 as the svalue that has state.
79
80 We call this stack-allocated region the "impl_reg". Allocating it on
81 the stack ensures that it is invalidated when the frame containing
82 the va_start returns, leading to
83 -Wanalyzer-use-of-pointer-in-stale-stack-frame on attempts to use such
84 a va_list.
85
86 To track svalues from variadic calls interprocedurally, we implement
87 variadic arguments via new child regions of the callee's frame_region,
88 var_arg_region, each one representing a storage slot for one of the
89 variadic arguments, accessed by index.
90
91 We have:
92
93 stack frame:
94 va_list: &impl_reg
95 'impl_reg': pointer to next var_arg_region
96 var_arg_region for arg 0
97 ...
98 var_arg_region for arg N-1
99
100 Hence given test_1 in stdarg-1.c, at the call to:
101
102 __analyzer_called_by_test_1 (int placeholder, ...);
103
104 here:
105
106 __analyzer_called_by_test_1 (42, "foo", 1066, '@');
107
108 we push this frame for the called function:
109 clusters within frame: ‘__analyzer_called_by_test_1’@2
110 cluster for: placeholder: (int)42
111 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0): &"foo" (TOUCHED)
112 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 1): (int)1066 (TOUCHED)
113 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 2): (int)64 (TOUCHED)
114 where the called function's frame has been populated with both the value
115 of the regular argument "placeholder", and with values for 3 variadic
116 arguments.
117
118 At the call to
119 va_start (ap, placeholder);
120 we allocate a region ALLOCA_REGION for ap to point to, populate that
121 region with the address of variadic argument 0, and set sm-state of
122 &ALLOCA_REGION to "started":
123 clusters within frame: ‘__analyzer_called_by_test_1’@2
124 cluster for: placeholder: (int)42
125 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0): &"foo" (TOUCHED)
126 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 1): (int)1066 (TOUCHED)
127 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 2): (int)64 (TOUCHED)
128 cluster for: ap: &ALLOCA_REGION
129 cluster for: ALLOCA_REGION: &VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0) (TOUCHED)
130 va_list:
131 0x4c83700: &ALLOCA_REGION: started
132
133 At each call to
134 va_arg (ap, TYPE);
135 we can look within *ap, locate the region holding the next variadic
136 argument to be extracted, extract the svalue, and advance the index
137 by effectively updating *ap.
138
139 At the va_end, we can set &ALLOCA_REGION's state to "ended".
140
141 The various __builtin_va_* accept ap by pointer, so we have e.g.:
142
143 __builtin_va_start (&ap, [...]);
144
145 except for the 2nd param of __builtin_va_copy, where the type
146 is already target-dependent (see the discussion of BT_VALIST_ARG
147 below). */
148
149/* Get a tree for diagnostics.
150 Typically we have "&ap", but it will make more sense to
151 the user as just "ap", so strip off the ADDR_EXPR. */
152
153static tree
154get_va_list_diag_arg (tree va_list_tree)
155{
156 if (TREE_CODE (va_list_tree) == ADDR_EXPR)
157 va_list_tree = TREE_OPERAND (va_list_tree, 0);
158 return va_list_tree;
159}
160
161/* Get argument ARG_IDX of type BT_VALIST_ARG (for use by va_copy).
162
163 builtin-types.def has:
164 DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
165
166 and c_common_nodes_and_builtins initializes va_list_arg_type_node
167 based on whether TREE_CODE (va_list_type_node) is of ARRAY_TYPE or
168 not, giving either one or zero levels of indirection. */
169
170static const svalue *
171get_BT_VALIST_ARG (const region_model *model,
172 region_model_context *ctxt,
173 const gcall *call,
174 unsigned arg_idx)
175{
176 tree arg = gimple_call_arg (call, arg_idx);
177 const svalue *arg_sval = model->get_rvalue (arg, ctxt);
178 if (const svalue *cast = arg_sval->maybe_undo_cast ())
179 arg_sval = cast;
180 if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
181 {
182 /* va_list_arg_type_node is a pointer to a va_list;
183 return *ARG_SVAL. */
184 const region *src_reg = model->deref_rvalue (arg_sval, arg, ctxt);
185 const svalue *src_reg_sval = model->get_store_value (src_reg, ctxt);
186 if (const svalue *cast = src_reg_sval->maybe_undo_cast ())
187 src_reg_sval = cast;
188 return src_reg_sval;
189 }
190 else
191 {
192 /* va_list_arg_type_node is a va_list; return ARG_SVAL. */
193 return arg_sval;
194 }
195}
196
197namespace {
198
199/* A state machine for tracking the state of a va_list, so that
200 we can enforce that each va_start is paired with a va_end,
201 and va_arg only happens within a va_start/va_end pair.
202 Specifically, this tracks the state of the &ALLOCA_BUFFER
203 that va_start/va_copy allocate. */
204
205class va_list_state_machine : public state_machine
206{
207public:
208 va_list_state_machine (logger *logger);
209
ff171cb1 210 bool inherited_state_p () const final override { return false; }
2402dc6b
DM
211
212 bool on_stmt (sm_context *sm_ctxt,
213 const supernode *node,
ff171cb1 214 const gimple *stmt) const final override;
2402dc6b 215
ff171cb1 216 bool can_purge_p (state_t s) const final override
2402dc6b
DM
217 {
218 return s != m_started;
219 }
ff171cb1 220 pending_diagnostic *on_leak (tree var) const final override;
2402dc6b
DM
221
222 /* State for a va_list that the result of a va_start or va_copy. */
223 state_t m_started;
224
225 /* State for a va_list that has had va_end called on it. */
226 state_t m_ended;
227
228private:
229 void on_va_start (sm_context *sm_ctxt, const supernode *node,
230 const gcall *call) const;
231 void on_va_copy (sm_context *sm_ctxt, const supernode *node,
232 const gcall *call) const;
233 void on_va_arg (sm_context *sm_ctxt, const supernode *node,
234 const gcall *call) const;
235 void on_va_end (sm_context *sm_ctxt, const supernode *node,
236 const gcall *call) const;
237 void check_for_ended_va_list (sm_context *sm_ctxt,
238 const supernode *node,
239 const gcall *call,
240 const svalue *arg,
241 const char *usage_fnname) const;
242};
243
244/* va_list_state_machine's ctor. */
245
246va_list_state_machine::va_list_state_machine (logger *logger)
247: state_machine ("va_list", logger)
248{
249 m_started = add_state ("started");
250 m_ended = add_state ("ended");
251}
252
253/* Implementation of the various "va_*" functions for
254 va_list_state_machine. */
255
256bool
257va_list_state_machine::on_stmt (sm_context *sm_ctxt,
258 const supernode *node,
259 const gimple *stmt) const
260{
261 if (const gcall *call = dyn_cast <const gcall *> (stmt))
262 {
263 if (gimple_call_internal_p (call)
264 && gimple_call_internal_fn (call) == IFN_VA_ARG)
265 {
266 on_va_arg (sm_ctxt, node, call);
267 return false;
268 }
269
270 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
271 if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL)
272 && gimple_builtin_call_types_compatible_p (call, callee_fndecl))
273 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
274 {
275 default:
276 break;
277
278 case BUILT_IN_VA_START:
279 on_va_start (sm_ctxt, node, call);
280 break;
281
282 case BUILT_IN_VA_COPY:
283 on_va_copy (sm_ctxt, node, call);
284 break;
285
286 case BUILT_IN_VA_END:
287 on_va_end (sm_ctxt, node, call);
288 break;
289 }
290 }
291 return false;
292}
293
294/* Get the svalue for which va_list_state_machine holds state on argument ARG_
295 IDX to CALL. */
296
297static const svalue *
298get_stateful_arg (sm_context *sm_ctxt, const gcall *call, unsigned arg_idx)
299{
300 tree ap = gimple_call_arg (call, arg_idx);
301 if (ap
302 && POINTER_TYPE_P (TREE_TYPE (ap)))
303 {
304 if (const program_state *new_state = sm_ctxt->get_new_program_state ())
305 {
306 const region_model *new_model = new_state->m_region_model;
307 const svalue *ptr_sval = new_model->get_rvalue (ap, NULL);
308 const region *reg = new_model->deref_rvalue (ptr_sval, ap, NULL);
309 const svalue *impl_sval = new_model->get_store_value (reg, NULL);
310 if (const svalue *cast = impl_sval->maybe_undo_cast ())
311 impl_sval = cast;
312 return impl_sval;
313 }
314 }
315 return NULL;
316}
317
318/* Abstract class for diagnostics relating to va_list_state_machine. */
319
320class va_list_sm_diagnostic : public pending_diagnostic
321{
322public:
ff171cb1 323 bool subclass_equal_p (const pending_diagnostic &base_other) const override
2402dc6b
DM
324 {
325 const va_list_sm_diagnostic &other
326 = (const va_list_sm_diagnostic &)base_other;
327 return (m_ap_sval == other.m_ap_sval
328 && same_tree_p (m_ap_tree, other.m_ap_tree));
329 }
330
331 label_text describe_state_change (const evdesc::state_change &change)
ff171cb1 332 override
2402dc6b
DM
333 {
334 if (const char *fnname = maybe_get_fnname (change))
335 return change.formatted_print ("%qs called here", fnname);
336 return label_text ();
337 }
338
6cf276dd
DM
339 diagnostic_event::meaning
340 get_meaning_for_state_change (const evdesc::state_change &change)
341 const final override
342 {
343 if (change.m_new_state == m_sm.m_started)
344 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
345 diagnostic_event::NOUN_resource);
346 if (change.m_new_state == m_sm.m_ended)
347 return diagnostic_event::meaning (diagnostic_event::VERB_release,
348 diagnostic_event::NOUN_resource);
349 return diagnostic_event::meaning ();
350 }
351
2402dc6b
DM
352protected:
353 va_list_sm_diagnostic (const va_list_state_machine &sm,
354 const svalue *ap_sval, tree ap_tree)
355 : m_sm (sm), m_ap_sval (ap_sval), m_ap_tree (ap_tree)
356 {}
357
358 static const char *maybe_get_fnname (const evdesc::state_change &change)
359 {
360 if (change.m_event.m_stmt)
361 if (const gcall *call = as_a <const gcall *> (change.m_event.m_stmt))
362 if (tree callee_fndecl = gimple_call_fndecl (call))
363 {
364 if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL))
365 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
366 {
367 case BUILT_IN_VA_START:
368 return "va_start";
369 case BUILT_IN_VA_COPY:
370 return "va_copy";
371 case BUILT_IN_VA_END:
372 return "va_end";
373 }
374 }
375 return NULL;
376 }
377
378 const va_list_state_machine &m_sm;
379 const svalue *m_ap_sval;
380 tree m_ap_tree;
381};
382
383/* Concrete class for -Wanalyzer-va-list-use-after-va-end:
384 complain about use of a va_list after va_end has been called on it. */
385
386class va_list_use_after_va_end : public va_list_sm_diagnostic
387{
388public:
389 va_list_use_after_va_end (const va_list_state_machine &sm,
390 const svalue *ap_sval, tree ap_tree,
391 const char *usage_fnname)
392 : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
393 m_usage_fnname (usage_fnname)
394 {
395 }
396
ff171cb1 397 int get_controlling_option () const final override
2402dc6b
DM
398 {
399 return OPT_Wanalyzer_va_list_use_after_va_end;
400 }
401
402 bool operator== (const va_list_use_after_va_end &other) const
403 {
404 return (va_list_sm_diagnostic::subclass_equal_p (other)
405 && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
406 }
407
ff171cb1 408 bool emit (rich_location *rich_loc) final override
2402dc6b
DM
409 {
410 auto_diagnostic_group d;
411 return warning_at (rich_loc, get_controlling_option (),
412 "%qs after %qs", m_usage_fnname, "va_end");
413 }
414
ff171cb1 415 const char *get_kind () const final override
2402dc6b
DM
416 {
417 return "va_list_use_after_va_end";
418 }
419
420 label_text describe_state_change (const evdesc::state_change &change)
ff171cb1 421 final override
2402dc6b
DM
422 {
423 if (change.m_new_state == m_sm.m_ended)
424 m_va_end_event = change.m_event_id;
425 return va_list_sm_diagnostic::describe_state_change (change);
426 }
427
ff171cb1 428 label_text describe_final_event (const evdesc::final_event &ev) final override
2402dc6b
DM
429 {
430 if (ev.m_expr)
431 {
432 if (m_va_end_event.known_p ())
433 return ev.formatted_print
434 ("%qs on %qE after %qs at %@",
435 m_usage_fnname, ev.m_expr, "va_end", &m_va_end_event);
436 else
437 return ev.formatted_print
438 ("%qs on %qE after %qs",
439 m_usage_fnname, ev.m_expr, "va_end");
440 }
441 else
442 {
443 if (m_va_end_event.known_p ())
444 return ev.formatted_print
445 ("%qs after %qs at %@",
446 m_usage_fnname, "va_end", &m_va_end_event);
447 else
448 return ev.formatted_print
449 ("%qs after %qs",
450 m_usage_fnname, "va_end");
451 }
452 }
453
454private:
455 diagnostic_event_id_t m_va_end_event;
456 const char *m_usage_fnname;
457};
458
459/* Concrete class for -Wanalyzer-va-list-leak:
460 complain about a va_list in the "started" state that doesn't get after
461 va_end called on it. */
462
463class va_list_leak : public va_list_sm_diagnostic
464{
465public:
466 va_list_leak (const va_list_state_machine &sm,
467 const svalue *ap_sval, tree ap_tree)
468 : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
469 m_start_event_fnname (NULL)
470 {
471 }
472
ff171cb1 473 int get_controlling_option () const final override
2402dc6b
DM
474 {
475 return OPT_Wanalyzer_va_list_leak;
476 }
477
478 bool operator== (const va_list_leak &other) const
479 {
480 return va_list_sm_diagnostic::subclass_equal_p (other);
481 }
482
2ac1459f 483 bool emit (rich_location *rich_loc) final override
2402dc6b
DM
484 {
485 auto_diagnostic_group d;
486 return warning_at (rich_loc, get_controlling_option (),
487 "missing call to %qs", "va_end");
488 }
489
ff171cb1 490 const char *get_kind () const final override { return "va_list_leak"; }
2402dc6b
DM
491
492 label_text describe_state_change (const evdesc::state_change &change)
ff171cb1 493 final override
2402dc6b
DM
494 {
495 if (change.m_new_state == m_sm.m_started)
496 {
497 m_start_event = change.m_event_id;
498 m_start_event_fnname = maybe_get_fnname (change);
499 }
500 return va_list_sm_diagnostic::describe_state_change (change);
501 }
502
ff171cb1 503 label_text describe_final_event (const evdesc::final_event &ev) final override
2402dc6b
DM
504 {
505 if (ev.m_expr)
506 {
507 if (m_start_event.known_p () && m_start_event_fnname)
508 return ev.formatted_print
509 ("missing call to %qs on %qE to match %qs at %@",
510 "va_end", ev.m_expr, m_start_event_fnname, &m_start_event);
511 else
512 return ev.formatted_print
513 ("missing call to %qs on %qE",
514 "va_end", ev.m_expr);
515 }
516 else
517 {
518 if (m_start_event.known_p () && m_start_event_fnname)
519 return ev.formatted_print
520 ("missing call to %qs to match %qs at %@",
521 "va_end", m_start_event_fnname, &m_start_event);
522 else
523 return ev.formatted_print
524 ("missing call to %qs",
525 "va_end");
526 }
527 }
528
529private:
530 diagnostic_event_id_t m_start_event;
531 const char *m_start_event_fnname;
532};
533
534/* Update state machine for a "va_start" call. */
535
536void
537va_list_state_machine::on_va_start (sm_context *sm_ctxt,
538 const supernode *,
539 const gcall *call) const
540{
541 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
542 if (arg)
543 {
544 /* Transition from start state to "started". */
545 if (sm_ctxt->get_state (call, arg) == m_start)
546 sm_ctxt->set_next_state (call, arg, m_started);
547 }
548}
549
550/* Complain if ARG is in the "ended" state. */
551
552void
553va_list_state_machine::check_for_ended_va_list (sm_context *sm_ctxt,
554 const supernode *node,
555 const gcall *call,
556 const svalue *arg,
557 const char *usage_fnname) const
558{
559 if (sm_ctxt->get_state (call, arg) == m_ended)
560 sm_ctxt->warn (node, call, arg,
561 new va_list_use_after_va_end (*this, arg, NULL_TREE,
562 usage_fnname));
563}
564
565/* Get the svalue with associated va_list_state_machine state for a
566 BT_VALIST_ARG for ARG_IDX of CALL, if SM_CTXT supports this,
567 or NULL otherwise. */
568
569static const svalue *
570get_stateful_BT_VALIST_ARG (sm_context *sm_ctxt,
571 const gcall *call,
572 unsigned arg_idx)
573{
574 if (const program_state *new_state = sm_ctxt->get_new_program_state ())
575 {
576 const region_model *new_model = new_state->m_region_model;
577 const svalue *arg = get_BT_VALIST_ARG (new_model, NULL, call, arg_idx);
578 return arg;
579 }
580 return NULL;
581}
582
583/* Update state machine for a "va_copy" call. */
584
585void
586va_list_state_machine::on_va_copy (sm_context *sm_ctxt,
587 const supernode *node,
588 const gcall *call) const
589{
590 const svalue *src_arg = get_stateful_BT_VALIST_ARG (sm_ctxt, call, 1);
591 if (src_arg)
592 check_for_ended_va_list (sm_ctxt, node, call, src_arg, "va_copy");
593
594 const svalue *dst_arg = get_stateful_arg (sm_ctxt, call, 0);
595 if (dst_arg)
596 {
597 /* Transition from start state to "started". */
598 if (sm_ctxt->get_state (call, dst_arg) == m_start)
599 sm_ctxt->set_next_state (call, dst_arg, m_started);
600 }
601}
602
603/* Update state machine for a "va_arg" call. */
604
605void
606va_list_state_machine::on_va_arg (sm_context *sm_ctxt,
607 const supernode *node,
608 const gcall *call) const
609{
610 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
611 if (arg)
612 check_for_ended_va_list (sm_ctxt, node, call, arg, "va_arg");
613}
614
615/* Update state machine for a "va_end" call. */
616
617void
618va_list_state_machine::on_va_end (sm_context *sm_ctxt,
619 const supernode *node,
620 const gcall *call) const
621{
622 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
623 if (arg)
624 {
625 state_t s = sm_ctxt->get_state (call, arg);
626 /* Transition from "started" to "ended". */
627 if (s == m_started)
628 sm_ctxt->set_next_state (call, arg, m_ended);
629 else if (s == m_ended)
630 check_for_ended_va_list (sm_ctxt, node, call, arg, "va_end");
631 }
632}
633
634/* Implementation of state_machine::on_leak vfunc for va_list_state_machine
635 (for complaining about leaks of values in state 'started'). */
636
637pending_diagnostic *
638va_list_state_machine::on_leak (tree var) const
639{
640 return new va_list_leak (*this, NULL, var);
641}
642
643} // anonymous namespace
644
645/* Internal interface to this file. */
646
647state_machine *
648make_va_list_state_machine (logger *logger)
649{
650 return new va_list_state_machine (logger);
651}
652
653/* Handle the on_call_pre part of "__builtin_va_start". */
654
655void
656region_model::impl_call_va_start (const call_details &cd)
657{
658 const svalue *out_ptr = cd.get_arg_svalue (0);
659 const region *out_reg
660 = deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
661
662 /* "*out_ptr = &IMPL_REGION;". */
663 const region *impl_reg = m_mgr->create_region_for_alloca (m_current_frame);
664
665 /* We abuse the types here, since va_list_type isn't
666 necessarily anything to do with a pointer. */
667 const svalue *ptr_to_impl_reg = m_mgr->get_ptr_svalue (NULL_TREE, impl_reg);
668 set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ());
669
670 /* "*(&IMPL_REGION) = VA_LIST_VAL (0);". */
671 const region *init_var_arg_reg
672 = m_mgr->get_var_arg_region (get_current_frame (), 0);
673 const svalue *ap_sval = m_mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg);
674 set_value (impl_reg, ap_sval, cd.get_ctxt ());
675}
676
677/* Handle the on_call_pre part of "__builtin_va_copy". */
678
679void
680region_model::impl_call_va_copy (const call_details &cd)
681{
682 const svalue *out_dst_ptr = cd.get_arg_svalue (0);
683 const svalue *in_va_list
684 = get_BT_VALIST_ARG (this, cd.get_ctxt (), cd.get_call_stmt (), 1);
685 in_va_list = check_for_poison (in_va_list,
686 get_va_list_diag_arg (cd.get_arg_tree (1)),
687 cd.get_ctxt ());
688
689 const region *out_dst_reg
690 = deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
691
692 /* "*out_dst_ptr = &NEW_IMPL_REGION;". */
693 const region *new_impl_reg
694 = m_mgr->create_region_for_alloca (m_current_frame);
695 const svalue *ptr_to_new_impl_reg
696 = m_mgr->get_ptr_svalue (NULL_TREE, new_impl_reg);
697 set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ());
698
699 if (const region *old_impl_reg = in_va_list->maybe_get_region ())
700 {
701
702 /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);". */
703 const svalue *existing_sval
704 = get_store_value (old_impl_reg, cd.get_ctxt ());
705 set_value (new_impl_reg, existing_sval, cd.get_ctxt ());
706 }
707}
708
709/* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT. */
710
711static int
712get_num_variadic_arguments (tree callee_fndecl,
713 const gcall *call_stmt)
714{
715 int num_positional = 0;
716 for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
717 iter_parm = DECL_CHAIN (iter_parm))
718 num_positional++;
719 return gimple_call_num_args (call_stmt) - num_positional;
720}
721
722/* An abstract subclass of pending_diagnostic for diagnostics relating
723 to bad va_arg invocations.
724
725 This shows the number of variadic arguments at the call of interest.
726 Ideally we'd also be able to highlight individual arguments, but
727 that location information isn't generally available from the middle end. */
728
729class va_arg_diagnostic : public pending_diagnostic
730{
731public:
732 /* Override of pending_diagnostic::add_call_event,
733 adding a custom call_event subclass. */
734 void add_call_event (const exploded_edge &eedge,
ff171cb1 735 checker_path *emission_path) override
2402dc6b
DM
736 {
737 /* As per call_event, but show the number of variadic arguments
738 in the call. */
739 class va_arg_call_event : public call_event
740 {
741 public:
742 va_arg_call_event (const exploded_edge &eedge,
743 location_t loc, tree fndecl, int depth,
744 int num_variadic_arguments)
745 : call_event (eedge, loc, fndecl, depth),
746 m_num_variadic_arguments (num_variadic_arguments)
747 {
748 }
749
ff171cb1 750 label_text get_desc (bool can_colorize) const override
2402dc6b
DM
751 {
752 return make_label_text_n
753 (can_colorize, m_num_variadic_arguments,
754 "calling %qE from %qE with %i variadic argument",
755 "calling %qE from %qE with %i variadic arguments",
756 get_callee_fndecl (),
757 get_caller_fndecl (),
758 m_num_variadic_arguments);
759 }
760 private:
761 int m_num_variadic_arguments;
762 };
763
764 const frame_region *frame_reg = m_var_arg_reg->get_frame_region ();
765 const exploded_node *dst_node = eedge.m_dest;
766 if (dst_node->get_state ().m_region_model->get_current_frame ()
767 == frame_reg)
768 {
769 const exploded_node *src_node = eedge.m_src;
770 const program_point &src_point = src_node->get_point ();
771 const int src_stack_depth = src_point.get_stack_depth ();
772 const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
773 const gcall *call_stmt = as_a <const gcall *> (last_stmt);
774 int num_variadic_arguments
775 = get_num_variadic_arguments (dst_node->get_function ()->decl,
776 call_stmt);
777 emission_path->add_event
778 (new va_arg_call_event (eedge,
779 (last_stmt
780 ? last_stmt->location
781 : UNKNOWN_LOCATION),
782 src_point.get_fndecl (),
783 src_stack_depth,
784 num_variadic_arguments));
785 }
786 else
787 pending_diagnostic::add_call_event (eedge, emission_path);
788 }
789
790protected:
791 va_arg_diagnostic (tree va_list_tree, const var_arg_region *var_arg_reg)
792 : m_va_list_tree (va_list_tree), m_var_arg_reg (var_arg_reg)
793 {}
794
ff171cb1 795 bool subclass_equal_p (const pending_diagnostic &base_other) const override
2402dc6b
DM
796 {
797 const va_arg_diagnostic &other = (const va_arg_diagnostic &)base_other;
798 return (same_tree_p (m_va_list_tree, other.m_va_list_tree)
799 && m_var_arg_reg == other.m_var_arg_reg);
800 }
801
802 /* Get the number of arguments consumed so far from the va_list
803 (*before* this va_arg call). */
804 unsigned get_num_consumed () const
805 {
806 return m_var_arg_reg->get_index ();
807 }
808
809 /* Get a 1-based index of which variadic argument is being consumed. */
810 unsigned get_variadic_index_for_diagnostic () const
811 {
812 return get_num_consumed () + 1;
813 }
814
815 /* User-readable expr for the va_list argument to va_arg. */
816 tree m_va_list_tree;
817
818 /* The region that the va_arg attempted to access. */
819 const var_arg_region *m_var_arg_reg;
820};
821
822/* A subclass of pending_diagnostic for complaining about a type mismatch
823 between the result of:
824 va_arg (AP);
825 and the type of the argument that was passed to the variadic call. */
826
827class va_arg_type_mismatch : public va_arg_diagnostic
828{
829public:
830 va_arg_type_mismatch (tree va_list_tree, const var_arg_region *var_arg_reg,
831 tree expected_type, tree actual_type)
832 : va_arg_diagnostic (va_list_tree, var_arg_reg),
833 m_expected_type (expected_type), m_actual_type (actual_type)
834 {}
835
ff171cb1 836 const char *get_kind () const final override
2402dc6b
DM
837 {
838 return "va_arg_type_mismatch";
839 }
840
841 bool subclass_equal_p (const pending_diagnostic &base_other)
ff171cb1 842 const final override
2402dc6b
DM
843 {
844 if (!va_arg_diagnostic::subclass_equal_p (base_other))
845 return false;
846 const va_arg_type_mismatch &other
847 = (const va_arg_type_mismatch &)base_other;
848 return (same_tree_p (m_expected_type, other.m_expected_type)
849 && same_tree_p (m_actual_type, other.m_actual_type));
850 }
851
ff171cb1 852 int get_controlling_option () const final override
2402dc6b
DM
853 {
854 return OPT_Wanalyzer_va_arg_type_mismatch;
855 }
856
ff171cb1 857 bool emit (rich_location *rich_loc) final override
2402dc6b
DM
858 {
859 auto_diagnostic_group d;
f0da5f0a
DM
860 diagnostic_metadata m;
861 /* "CWE-686: Function Call With Incorrect Argument Type". */
862 m.add_cwe (686);
2402dc6b 863 bool warned
f0da5f0a
DM
864 = warning_meta (rich_loc, m, get_controlling_option (),
865 "%<va_arg%> expected %qT but received %qT"
866 " for variadic argument %i of %qE",
867 m_expected_type, m_actual_type,
868 get_variadic_index_for_diagnostic (), m_va_list_tree);
2402dc6b
DM
869 return warned;
870 }
871
ff171cb1 872 label_text describe_final_event (const evdesc::final_event &ev) final override
2402dc6b
DM
873 {
874 return ev.formatted_print ("%<va_arg%> expected %qT but received %qT"
875 " for variadic argument %i of %qE",
876 m_expected_type, m_actual_type,
877 get_variadic_index_for_diagnostic (),
878 m_va_list_tree);
879 }
880
881private:
882 tree m_expected_type;
883 tree m_actual_type;
884};
885
886/* A subclass of pending_diagnostic for complaining about a
887 va_arg (AP);
888 after all of the args in AP have been consumed. */
889
890class va_list_exhausted : public va_arg_diagnostic
891{
892public:
893 va_list_exhausted (tree va_list_tree, const var_arg_region *var_arg_reg)
894 : va_arg_diagnostic (va_list_tree, var_arg_reg)
895 {}
896
ff171cb1 897 const char *get_kind () const final override
2402dc6b
DM
898 {
899 return "va_list_exhausted";
900 }
901
ff171cb1 902 int get_controlling_option () const final override
2402dc6b
DM
903 {
904 return OPT_Wanalyzer_va_list_exhausted;
905 }
906
ff171cb1 907 bool emit (rich_location *rich_loc) final override
2402dc6b
DM
908 {
909 auto_diagnostic_group d;
f443024b
DM
910 diagnostic_metadata m;
911 /* CWE-685: Function Call With Incorrect Number of Arguments. */
912 m.add_cwe (685);
913 bool warned = warning_meta (rich_loc, m, get_controlling_option (),
914 "%qE has no more arguments (%i consumed)",
915 m_va_list_tree, get_num_consumed ());
2402dc6b
DM
916 return warned;
917 }
918
ff171cb1 919 label_text describe_final_event (const evdesc::final_event &ev) final override
2402dc6b
DM
920 {
921 return ev.formatted_print ("%qE has no more arguments (%i consumed)",
922 m_va_list_tree, get_num_consumed ());
923 }
924};
925
926/* Return true if it's OK to copy a value from ARG_TYPE to LHS_TYPE via
927 va_arg (where argument promotion has already happened). */
928
929static bool
930va_arg_compatible_types_p (tree lhs_type, tree arg_type)
931{
932 return compat_types_p (arg_type, lhs_type);
933}
934
935/* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
936 Otherwise return NULL. */
937
938static const var_arg_region *
939maybe_get_var_arg_region (const svalue *ap_sval)
940{
941 if (const region *reg = ap_sval->maybe_get_region ())
942 return reg->dyn_cast_var_arg_region ();
943 return NULL;
944}
945
946/* Handle the on_call_pre part of "__builtin_va_arg". */
947
948void
949region_model::impl_call_va_arg (const call_details &cd)
950{
951 region_model_context *ctxt = cd.get_ctxt ();
952
953 const svalue *in_ptr = cd.get_arg_svalue (0);
954 const region *ap_reg = deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt);
955
956 const svalue *ap_sval = get_store_value (ap_reg, ctxt);
957 if (const svalue *cast = ap_sval->maybe_undo_cast ())
958 ap_sval = cast;
959
960 tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0));
961 ap_sval = check_for_poison (ap_sval, va_list_tree, ctxt);
962
963 if (const region *impl_reg = ap_sval->maybe_get_region ())
964 {
965 const svalue *old_impl_sval = get_store_value (impl_reg, ctxt);
966 if (const var_arg_region *arg_reg
967 = maybe_get_var_arg_region (old_impl_sval))
968 {
969 bool saw_problem = false;
970
971 const frame_region *frame_reg = arg_reg->get_frame_region ();
972 unsigned next_arg_idx = arg_reg->get_index ();
973
b852aa7f 974 if (frame_reg->get_stack_depth () > 1)
2402dc6b
DM
975 {
976 /* The interprocedural case: the called frame will have been
977 populated with any variadic aruguments.
978 Attempt to extract arg_reg to cd's return region (which already
979 has a conjured_svalue), or warn if there's a problem
980 (incompatible types, or if we've run out of args). */
981 if (const svalue *arg_sval
982 = m_store.get_any_binding (m_mgr->get_store_manager (),
983 arg_reg))
984 {
985 tree lhs_type = cd.get_lhs_type ();
986 tree arg_type = arg_sval->get_type ();
987 if (va_arg_compatible_types_p (lhs_type, arg_type))
988 cd.maybe_set_lhs (arg_sval);
989 else
990 {
991 if (ctxt)
992 ctxt->warn (new va_arg_type_mismatch (va_list_tree,
993 arg_reg,
994 lhs_type,
995 arg_type));
996 saw_problem = true;
997 }
998 }
999 else
1000 {
1001 if (ctxt)
1002 ctxt->warn (new va_list_exhausted (va_list_tree, arg_reg));
1003 saw_problem = true;
1004 }
1005 }
1006 else
1007 {
1008 /* This frame is an entry-point to the analysis, so there won't be
1009 any specific var_arg_regions populated within it.
1010 We already have a conjured_svalue for the result, so leave
1011 it untouched. */
b852aa7f 1012 gcc_assert (frame_reg->get_stack_depth () == 1);
2402dc6b
DM
1013 }
1014
1015 if (saw_problem)
1016 {
1017 /* Set impl_reg to UNKNOWN to suppress further warnings. */
1018 const svalue *new_ap_sval
1019 = m_mgr->get_or_create_unknown_svalue (impl_reg->get_type ());
1020 set_value (impl_reg, new_ap_sval, ctxt);
1021 }
1022 else
1023 {
1024 /* Update impl_reg to advance to the next arg. */
1025 const region *next_var_arg_region
1026 = m_mgr->get_var_arg_region (frame_reg, next_arg_idx + 1);
1027 const svalue *new_ap_sval
1028 = m_mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region);
1029 set_value (impl_reg, new_ap_sval, ctxt);
1030 }
1031 }
1032 }
1033}
1034
1035/* Handle the on_call_post part of "__builtin_va_end". */
1036
1037void
1038region_model::impl_call_va_end (const call_details &)
1039{
1040 /* No-op. */
1041}
1042
1043} // namespace ana
1044
1045#endif /* #if ENABLE_ANALYZER */