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