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