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