]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/analyzer/varargs.cc
analyzer: implement four new warnings for <stdarg.h> misuses [PR105103]
[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
209 bool inherited_state_p () const FINAL OVERRIDE { return false; }
210
211 bool on_stmt (sm_context *sm_ctxt,
212 const supernode *node,
213 const gimple *stmt) const FINAL OVERRIDE;
214
215 bool can_purge_p (state_t s) const FINAL OVERRIDE
216 {
217 return s != m_started;
218 }
219 pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
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:
322 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
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)
331 OVERRIDE
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
338protected:
339 va_list_sm_diagnostic (const va_list_state_machine &sm,
340 const svalue *ap_sval, tree ap_tree)
341 : m_sm (sm), m_ap_sval (ap_sval), m_ap_tree (ap_tree)
342 {}
343
344 static const char *maybe_get_fnname (const evdesc::state_change &change)
345 {
346 if (change.m_event.m_stmt)
347 if (const gcall *call = as_a <const gcall *> (change.m_event.m_stmt))
348 if (tree callee_fndecl = gimple_call_fndecl (call))
349 {
350 if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL))
351 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
352 {
353 case BUILT_IN_VA_START:
354 return "va_start";
355 case BUILT_IN_VA_COPY:
356 return "va_copy";
357 case BUILT_IN_VA_END:
358 return "va_end";
359 }
360 }
361 return NULL;
362 }
363
364 const va_list_state_machine &m_sm;
365 const svalue *m_ap_sval;
366 tree m_ap_tree;
367};
368
369/* Concrete class for -Wanalyzer-va-list-use-after-va-end:
370 complain about use of a va_list after va_end has been called on it. */
371
372class va_list_use_after_va_end : public va_list_sm_diagnostic
373{
374public:
375 va_list_use_after_va_end (const va_list_state_machine &sm,
376 const svalue *ap_sval, tree ap_tree,
377 const char *usage_fnname)
378 : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
379 m_usage_fnname (usage_fnname)
380 {
381 }
382
383 int get_controlling_option () const FINAL OVERRIDE
384 {
385 return OPT_Wanalyzer_va_list_use_after_va_end;
386 }
387
388 bool operator== (const va_list_use_after_va_end &other) const
389 {
390 return (va_list_sm_diagnostic::subclass_equal_p (other)
391 && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
392 }
393
394 bool emit (rich_location *rich_loc) FINAL OVERRIDE
395 {
396 auto_diagnostic_group d;
397 return warning_at (rich_loc, get_controlling_option (),
398 "%qs after %qs", m_usage_fnname, "va_end");
399 }
400
401 const char *get_kind () const FINAL OVERRIDE
402 {
403 return "va_list_use_after_va_end";
404 }
405
406 label_text describe_state_change (const evdesc::state_change &change)
407 FINAL OVERRIDE
408 {
409 if (change.m_new_state == m_sm.m_ended)
410 m_va_end_event = change.m_event_id;
411 return va_list_sm_diagnostic::describe_state_change (change);
412 }
413
414 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
415 {
416 if (ev.m_expr)
417 {
418 if (m_va_end_event.known_p ())
419 return ev.formatted_print
420 ("%qs on %qE after %qs at %@",
421 m_usage_fnname, ev.m_expr, "va_end", &m_va_end_event);
422 else
423 return ev.formatted_print
424 ("%qs on %qE after %qs",
425 m_usage_fnname, ev.m_expr, "va_end");
426 }
427 else
428 {
429 if (m_va_end_event.known_p ())
430 return ev.formatted_print
431 ("%qs after %qs at %@",
432 m_usage_fnname, "va_end", &m_va_end_event);
433 else
434 return ev.formatted_print
435 ("%qs after %qs",
436 m_usage_fnname, "va_end");
437 }
438 }
439
440private:
441 diagnostic_event_id_t m_va_end_event;
442 const char *m_usage_fnname;
443};
444
445/* Concrete class for -Wanalyzer-va-list-leak:
446 complain about a va_list in the "started" state that doesn't get after
447 va_end called on it. */
448
449class va_list_leak : public va_list_sm_diagnostic
450{
451public:
452 va_list_leak (const va_list_state_machine &sm,
453 const svalue *ap_sval, tree ap_tree)
454 : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
455 m_start_event_fnname (NULL)
456 {
457 }
458
459 int get_controlling_option () const FINAL OVERRIDE
460 {
461 return OPT_Wanalyzer_va_list_leak;
462 }
463
464 bool operator== (const va_list_leak &other) const
465 {
466 return va_list_sm_diagnostic::subclass_equal_p (other);
467 }
468
469 bool emit (rich_location *rich_loc)
470 {
471 auto_diagnostic_group d;
472 return warning_at (rich_loc, get_controlling_option (),
473 "missing call to %qs", "va_end");
474 }
475
476 const char *get_kind () const FINAL OVERRIDE { return "va_list_leak"; }
477
478 label_text describe_state_change (const evdesc::state_change &change)
479 FINAL OVERRIDE
480 {
481 if (change.m_new_state == m_sm.m_started)
482 {
483 m_start_event = change.m_event_id;
484 m_start_event_fnname = maybe_get_fnname (change);
485 }
486 return va_list_sm_diagnostic::describe_state_change (change);
487 }
488
489 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
490 {
491 if (ev.m_expr)
492 {
493 if (m_start_event.known_p () && m_start_event_fnname)
494 return ev.formatted_print
495 ("missing call to %qs on %qE to match %qs at %@",
496 "va_end", ev.m_expr, m_start_event_fnname, &m_start_event);
497 else
498 return ev.formatted_print
499 ("missing call to %qs on %qE",
500 "va_end", ev.m_expr);
501 }
502 else
503 {
504 if (m_start_event.known_p () && m_start_event_fnname)
505 return ev.formatted_print
506 ("missing call to %qs to match %qs at %@",
507 "va_end", m_start_event_fnname, &m_start_event);
508 else
509 return ev.formatted_print
510 ("missing call to %qs",
511 "va_end");
512 }
513 }
514
515private:
516 diagnostic_event_id_t m_start_event;
517 const char *m_start_event_fnname;
518};
519
520/* Update state machine for a "va_start" call. */
521
522void
523va_list_state_machine::on_va_start (sm_context *sm_ctxt,
524 const supernode *,
525 const gcall *call) const
526{
527 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
528 if (arg)
529 {
530 /* Transition from start state to "started". */
531 if (sm_ctxt->get_state (call, arg) == m_start)
532 sm_ctxt->set_next_state (call, arg, m_started);
533 }
534}
535
536/* Complain if ARG is in the "ended" state. */
537
538void
539va_list_state_machine::check_for_ended_va_list (sm_context *sm_ctxt,
540 const supernode *node,
541 const gcall *call,
542 const svalue *arg,
543 const char *usage_fnname) const
544{
545 if (sm_ctxt->get_state (call, arg) == m_ended)
546 sm_ctxt->warn (node, call, arg,
547 new va_list_use_after_va_end (*this, arg, NULL_TREE,
548 usage_fnname));
549}
550
551/* Get the svalue with associated va_list_state_machine state for a
552 BT_VALIST_ARG for ARG_IDX of CALL, if SM_CTXT supports this,
553 or NULL otherwise. */
554
555static const svalue *
556get_stateful_BT_VALIST_ARG (sm_context *sm_ctxt,
557 const gcall *call,
558 unsigned arg_idx)
559{
560 if (const program_state *new_state = sm_ctxt->get_new_program_state ())
561 {
562 const region_model *new_model = new_state->m_region_model;
563 const svalue *arg = get_BT_VALIST_ARG (new_model, NULL, call, arg_idx);
564 return arg;
565 }
566 return NULL;
567}
568
569/* Update state machine for a "va_copy" call. */
570
571void
572va_list_state_machine::on_va_copy (sm_context *sm_ctxt,
573 const supernode *node,
574 const gcall *call) const
575{
576 const svalue *src_arg = get_stateful_BT_VALIST_ARG (sm_ctxt, call, 1);
577 if (src_arg)
578 check_for_ended_va_list (sm_ctxt, node, call, src_arg, "va_copy");
579
580 const svalue *dst_arg = get_stateful_arg (sm_ctxt, call, 0);
581 if (dst_arg)
582 {
583 /* Transition from start state to "started". */
584 if (sm_ctxt->get_state (call, dst_arg) == m_start)
585 sm_ctxt->set_next_state (call, dst_arg, m_started);
586 }
587}
588
589/* Update state machine for a "va_arg" call. */
590
591void
592va_list_state_machine::on_va_arg (sm_context *sm_ctxt,
593 const supernode *node,
594 const gcall *call) const
595{
596 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
597 if (arg)
598 check_for_ended_va_list (sm_ctxt, node, call, arg, "va_arg");
599}
600
601/* Update state machine for a "va_end" call. */
602
603void
604va_list_state_machine::on_va_end (sm_context *sm_ctxt,
605 const supernode *node,
606 const gcall *call) const
607{
608 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
609 if (arg)
610 {
611 state_t s = sm_ctxt->get_state (call, arg);
612 /* Transition from "started" to "ended". */
613 if (s == m_started)
614 sm_ctxt->set_next_state (call, arg, m_ended);
615 else if (s == m_ended)
616 check_for_ended_va_list (sm_ctxt, node, call, arg, "va_end");
617 }
618}
619
620/* Implementation of state_machine::on_leak vfunc for va_list_state_machine
621 (for complaining about leaks of values in state 'started'). */
622
623pending_diagnostic *
624va_list_state_machine::on_leak (tree var) const
625{
626 return new va_list_leak (*this, NULL, var);
627}
628
629} // anonymous namespace
630
631/* Internal interface to this file. */
632
633state_machine *
634make_va_list_state_machine (logger *logger)
635{
636 return new va_list_state_machine (logger);
637}
638
639/* Handle the on_call_pre part of "__builtin_va_start". */
640
641void
642region_model::impl_call_va_start (const call_details &cd)
643{
644 const svalue *out_ptr = cd.get_arg_svalue (0);
645 const region *out_reg
646 = deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
647
648 /* "*out_ptr = &IMPL_REGION;". */
649 const region *impl_reg = m_mgr->create_region_for_alloca (m_current_frame);
650
651 /* We abuse the types here, since va_list_type isn't
652 necessarily anything to do with a pointer. */
653 const svalue *ptr_to_impl_reg = m_mgr->get_ptr_svalue (NULL_TREE, impl_reg);
654 set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ());
655
656 /* "*(&IMPL_REGION) = VA_LIST_VAL (0);". */
657 const region *init_var_arg_reg
658 = m_mgr->get_var_arg_region (get_current_frame (), 0);
659 const svalue *ap_sval = m_mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg);
660 set_value (impl_reg, ap_sval, cd.get_ctxt ());
661}
662
663/* Handle the on_call_pre part of "__builtin_va_copy". */
664
665void
666region_model::impl_call_va_copy (const call_details &cd)
667{
668 const svalue *out_dst_ptr = cd.get_arg_svalue (0);
669 const svalue *in_va_list
670 = get_BT_VALIST_ARG (this, cd.get_ctxt (), cd.get_call_stmt (), 1);
671 in_va_list = check_for_poison (in_va_list,
672 get_va_list_diag_arg (cd.get_arg_tree (1)),
673 cd.get_ctxt ());
674
675 const region *out_dst_reg
676 = deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
677
678 /* "*out_dst_ptr = &NEW_IMPL_REGION;". */
679 const region *new_impl_reg
680 = m_mgr->create_region_for_alloca (m_current_frame);
681 const svalue *ptr_to_new_impl_reg
682 = m_mgr->get_ptr_svalue (NULL_TREE, new_impl_reg);
683 set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ());
684
685 if (const region *old_impl_reg = in_va_list->maybe_get_region ())
686 {
687
688 /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);". */
689 const svalue *existing_sval
690 = get_store_value (old_impl_reg, cd.get_ctxt ());
691 set_value (new_impl_reg, existing_sval, cd.get_ctxt ());
692 }
693}
694
695/* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT. */
696
697static int
698get_num_variadic_arguments (tree callee_fndecl,
699 const gcall *call_stmt)
700{
701 int num_positional = 0;
702 for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
703 iter_parm = DECL_CHAIN (iter_parm))
704 num_positional++;
705 return gimple_call_num_args (call_stmt) - num_positional;
706}
707
708/* An abstract subclass of pending_diagnostic for diagnostics relating
709 to bad va_arg invocations.
710
711 This shows the number of variadic arguments at the call of interest.
712 Ideally we'd also be able to highlight individual arguments, but
713 that location information isn't generally available from the middle end. */
714
715class va_arg_diagnostic : public pending_diagnostic
716{
717public:
718 /* Override of pending_diagnostic::add_call_event,
719 adding a custom call_event subclass. */
720 void add_call_event (const exploded_edge &eedge,
721 checker_path *emission_path) OVERRIDE
722 {
723 /* As per call_event, but show the number of variadic arguments
724 in the call. */
725 class va_arg_call_event : public call_event
726 {
727 public:
728 va_arg_call_event (const exploded_edge &eedge,
729 location_t loc, tree fndecl, int depth,
730 int num_variadic_arguments)
731 : call_event (eedge, loc, fndecl, depth),
732 m_num_variadic_arguments (num_variadic_arguments)
733 {
734 }
735
736 label_text get_desc (bool can_colorize) const OVERRIDE
737 {
738 return make_label_text_n
739 (can_colorize, m_num_variadic_arguments,
740 "calling %qE from %qE with %i variadic argument",
741 "calling %qE from %qE with %i variadic arguments",
742 get_callee_fndecl (),
743 get_caller_fndecl (),
744 m_num_variadic_arguments);
745 }
746 private:
747 int m_num_variadic_arguments;
748 };
749
750 const frame_region *frame_reg = m_var_arg_reg->get_frame_region ();
751 const exploded_node *dst_node = eedge.m_dest;
752 if (dst_node->get_state ().m_region_model->get_current_frame ()
753 == frame_reg)
754 {
755 const exploded_node *src_node = eedge.m_src;
756 const program_point &src_point = src_node->get_point ();
757 const int src_stack_depth = src_point.get_stack_depth ();
758 const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
759 const gcall *call_stmt = as_a <const gcall *> (last_stmt);
760 int num_variadic_arguments
761 = get_num_variadic_arguments (dst_node->get_function ()->decl,
762 call_stmt);
763 emission_path->add_event
764 (new va_arg_call_event (eedge,
765 (last_stmt
766 ? last_stmt->location
767 : UNKNOWN_LOCATION),
768 src_point.get_fndecl (),
769 src_stack_depth,
770 num_variadic_arguments));
771 }
772 else
773 pending_diagnostic::add_call_event (eedge, emission_path);
774 }
775
776protected:
777 va_arg_diagnostic (tree va_list_tree, const var_arg_region *var_arg_reg)
778 : m_va_list_tree (va_list_tree), m_var_arg_reg (var_arg_reg)
779 {}
780
781 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
782 {
783 const va_arg_diagnostic &other = (const va_arg_diagnostic &)base_other;
784 return (same_tree_p (m_va_list_tree, other.m_va_list_tree)
785 && m_var_arg_reg == other.m_var_arg_reg);
786 }
787
788 /* Get the number of arguments consumed so far from the va_list
789 (*before* this va_arg call). */
790 unsigned get_num_consumed () const
791 {
792 return m_var_arg_reg->get_index ();
793 }
794
795 /* Get a 1-based index of which variadic argument is being consumed. */
796 unsigned get_variadic_index_for_diagnostic () const
797 {
798 return get_num_consumed () + 1;
799 }
800
801 /* User-readable expr for the va_list argument to va_arg. */
802 tree m_va_list_tree;
803
804 /* The region that the va_arg attempted to access. */
805 const var_arg_region *m_var_arg_reg;
806};
807
808/* A subclass of pending_diagnostic for complaining about a type mismatch
809 between the result of:
810 va_arg (AP);
811 and the type of the argument that was passed to the variadic call. */
812
813class va_arg_type_mismatch : public va_arg_diagnostic
814{
815public:
816 va_arg_type_mismatch (tree va_list_tree, const var_arg_region *var_arg_reg,
817 tree expected_type, tree actual_type)
818 : va_arg_diagnostic (va_list_tree, var_arg_reg),
819 m_expected_type (expected_type), m_actual_type (actual_type)
820 {}
821
822 const char *get_kind () const FINAL OVERRIDE
823 {
824 return "va_arg_type_mismatch";
825 }
826
827 bool subclass_equal_p (const pending_diagnostic &base_other)
828 const FINAL OVERRIDE
829 {
830 if (!va_arg_diagnostic::subclass_equal_p (base_other))
831 return false;
832 const va_arg_type_mismatch &other
833 = (const va_arg_type_mismatch &)base_other;
834 return (same_tree_p (m_expected_type, other.m_expected_type)
835 && same_tree_p (m_actual_type, other.m_actual_type));
836 }
837
838 int get_controlling_option () const FINAL OVERRIDE
839 {
840 return OPT_Wanalyzer_va_arg_type_mismatch;
841 }
842
843 bool emit (rich_location *rich_loc) FINAL OVERRIDE
844 {
845 auto_diagnostic_group d;
846 bool warned
847 = warning_at (rich_loc, get_controlling_option (),
848 "%<va_arg%> expected %qT but received %qT"
849 " for variadic argument %i of %qE",
850 m_expected_type, m_actual_type,
851 get_variadic_index_for_diagnostic (), m_va_list_tree);
852 return warned;
853 }
854
855 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
856 {
857 return ev.formatted_print ("%<va_arg%> expected %qT but received %qT"
858 " for variadic argument %i of %qE",
859 m_expected_type, m_actual_type,
860 get_variadic_index_for_diagnostic (),
861 m_va_list_tree);
862 }
863
864private:
865 tree m_expected_type;
866 tree m_actual_type;
867};
868
869/* A subclass of pending_diagnostic for complaining about a
870 va_arg (AP);
871 after all of the args in AP have been consumed. */
872
873class va_list_exhausted : public va_arg_diagnostic
874{
875public:
876 va_list_exhausted (tree va_list_tree, const var_arg_region *var_arg_reg)
877 : va_arg_diagnostic (va_list_tree, var_arg_reg)
878 {}
879
880 const char *get_kind () const FINAL OVERRIDE
881 {
882 return "va_list_exhausted";
883 }
884
885 int get_controlling_option () const FINAL OVERRIDE
886 {
887 return OPT_Wanalyzer_va_list_exhausted;
888 }
889
890 bool emit (rich_location *rich_loc) FINAL OVERRIDE
891 {
892 auto_diagnostic_group d;
893 bool warned = warning_at (rich_loc, get_controlling_option (),
894 "%qE has no more arguments (%i consumed)",
895 m_va_list_tree, get_num_consumed ());
896 return warned;
897 }
898
899 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
900 {
901 return ev.formatted_print ("%qE has no more arguments (%i consumed)",
902 m_va_list_tree, get_num_consumed ());
903 }
904};
905
906/* Return true if it's OK to copy a value from ARG_TYPE to LHS_TYPE via
907 va_arg (where argument promotion has already happened). */
908
909static bool
910va_arg_compatible_types_p (tree lhs_type, tree arg_type)
911{
912 return compat_types_p (arg_type, lhs_type);
913}
914
915/* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
916 Otherwise return NULL. */
917
918static const var_arg_region *
919maybe_get_var_arg_region (const svalue *ap_sval)
920{
921 if (const region *reg = ap_sval->maybe_get_region ())
922 return reg->dyn_cast_var_arg_region ();
923 return NULL;
924}
925
926/* Handle the on_call_pre part of "__builtin_va_arg". */
927
928void
929region_model::impl_call_va_arg (const call_details &cd)
930{
931 region_model_context *ctxt = cd.get_ctxt ();
932
933 const svalue *in_ptr = cd.get_arg_svalue (0);
934 const region *ap_reg = deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt);
935
936 const svalue *ap_sval = get_store_value (ap_reg, ctxt);
937 if (const svalue *cast = ap_sval->maybe_undo_cast ())
938 ap_sval = cast;
939
940 tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0));
941 ap_sval = check_for_poison (ap_sval, va_list_tree, ctxt);
942
943 if (const region *impl_reg = ap_sval->maybe_get_region ())
944 {
945 const svalue *old_impl_sval = get_store_value (impl_reg, ctxt);
946 if (const var_arg_region *arg_reg
947 = maybe_get_var_arg_region (old_impl_sval))
948 {
949 bool saw_problem = false;
950
951 const frame_region *frame_reg = arg_reg->get_frame_region ();
952 unsigned next_arg_idx = arg_reg->get_index ();
953
954 if (get_stack_depth () > 1)
955 {
956 /* The interprocedural case: the called frame will have been
957 populated with any variadic aruguments.
958 Attempt to extract arg_reg to cd's return region (which already
959 has a conjured_svalue), or warn if there's a problem
960 (incompatible types, or if we've run out of args). */
961 if (const svalue *arg_sval
962 = m_store.get_any_binding (m_mgr->get_store_manager (),
963 arg_reg))
964 {
965 tree lhs_type = cd.get_lhs_type ();
966 tree arg_type = arg_sval->get_type ();
967 if (va_arg_compatible_types_p (lhs_type, arg_type))
968 cd.maybe_set_lhs (arg_sval);
969 else
970 {
971 if (ctxt)
972 ctxt->warn (new va_arg_type_mismatch (va_list_tree,
973 arg_reg,
974 lhs_type,
975 arg_type));
976 saw_problem = true;
977 }
978 }
979 else
980 {
981 if (ctxt)
982 ctxt->warn (new va_list_exhausted (va_list_tree, arg_reg));
983 saw_problem = true;
984 }
985 }
986 else
987 {
988 /* This frame is an entry-point to the analysis, so there won't be
989 any specific var_arg_regions populated within it.
990 We already have a conjured_svalue for the result, so leave
991 it untouched. */
992 gcc_assert (get_stack_depth () == 1);
993 }
994
995 if (saw_problem)
996 {
997 /* Set impl_reg to UNKNOWN to suppress further warnings. */
998 const svalue *new_ap_sval
999 = m_mgr->get_or_create_unknown_svalue (impl_reg->get_type ());
1000 set_value (impl_reg, new_ap_sval, ctxt);
1001 }
1002 else
1003 {
1004 /* Update impl_reg to advance to the next arg. */
1005 const region *next_var_arg_region
1006 = m_mgr->get_var_arg_region (frame_reg, next_arg_idx + 1);
1007 const svalue *new_ap_sval
1008 = m_mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region);
1009 set_value (impl_reg, new_ap_sval, ctxt);
1010 }
1011 }
1012 }
1013}
1014
1015/* Handle the on_call_post part of "__builtin_va_end". */
1016
1017void
1018region_model::impl_call_va_end (const call_details &)
1019{
1020 /* No-op. */
1021}
1022
1023} // namespace ana
1024
1025#endif /* #if ENABLE_ANALYZER */