1 /* Implementation of <stdarg.h> within analyzer.
2 Copyright (C) 2022 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
25 #include "make-unique.h"
28 #include "basic-block.h"
30 #include "diagnostic-path.h"
31 #include "analyzer/analyzer.h"
32 #include "analyzer/analyzer-logging.h"
33 #include "analyzer/sm.h"
34 #include "analyzer/pending-diagnostic.h"
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"
41 #include "analyzer/supergraph.h"
42 #include "analyzer/diagnostic-manager.h"
43 #include "analyzer/exploded-graph.h"
44 #include "diagnostic-metadata.h"
50 /* Implementation of <stdarg.h> within analyzer.
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
61 The analyzer runs *before* the "stdarg" and "lower_vaarg" gimple
62 passes, which have target-dependent effects.
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.
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
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.
86 'impl_reg': pointer to next var_arg_region
87 var_arg_region for arg 0
89 var_arg_region for arg N-1
91 Hence given test_1 in stdarg-1.c, at the call to:
93 __analyzer_called_by_test_1 (int placeholder, ...);
97 __analyzer_called_by_test_1 (42, "foo", 1066, '@');
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
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)
122 0x4c83700: &ALLOCA_REGION: started
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.
130 At the va_end, we can set &ALLOCA_REGION's state to "ended".
132 The various __builtin_va_* accept ap by pointer, so we have e.g.:
134 __builtin_va_start (&ap, [...]);
136 except for the 2nd param of __builtin_va_copy, where the type
137 is already target-dependent (see the discussion of get_va_copy_arg
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. */
145 get_va_list_diag_arg (tree va_list_tree)
147 if (TREE_CODE (va_list_tree) == ADDR_EXPR)
148 va_list_tree = TREE_OPERAND (va_list_tree, 0);
152 /* Get argument ARG_IDX of va_copy.
154 builtin-types.def has:
155 DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
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
159 not, giving either one or zero levels of indirection.
161 Alternatively we could be dealing with __builtin_ms_va_copy or
162 __builtin_sysv_va_copy.
164 Handle this by looking at the types of the argument in question. */
166 static const svalue *
167 get_va_copy_arg (const region_model *model,
168 region_model_context *ctxt,
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 ())
176 if (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE
177 && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == ARRAY_TYPE)
179 /* va_list_arg_type_node is a pointer to a va_list;
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 ())
189 /* va_list_arg_type_node is a va_list; return ARG_SVAL. */
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. */
202 class va_list_state_machine : public state_machine
205 va_list_state_machine (logger *logger);
207 bool inherited_state_p () const final override { return false; }
209 bool on_stmt (sm_context *sm_ctxt,
210 const supernode *node,
211 const gimple *stmt) const final override;
213 bool can_purge_p (state_t s) const final override
215 return s != m_started;
217 std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
219 /* State for a va_list that is the result of a va_start or va_copy. */
222 /* State for a va_list that has had va_end called on it. */
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,
238 const char *usage_fnname) const;
241 /* va_list_state_machine's ctor. */
243 va_list_state_machine::va_list_state_machine (logger *logger)
244 : state_machine ("va_list", logger)
246 m_started = add_state ("started");
247 m_ended = add_state ("ended");
250 /* Implementation of the various "va_*" functions for
251 va_list_state_machine. */
254 va_list_state_machine::on_stmt (sm_context *sm_ctxt,
255 const supernode *node,
256 const gimple *stmt) const
258 if (const gcall *call = dyn_cast <const gcall *> (stmt))
260 if (gimple_call_internal_p (call)
261 && gimple_call_internal_fn (call) == IFN_VA_ARG)
263 on_va_arg (sm_ctxt, node, call);
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))
275 case BUILT_IN_VA_START:
276 on_va_start (sm_ctxt, node, call);
279 case BUILT_IN_VA_COPY:
280 on_va_copy (sm_ctxt, node, call);
283 case BUILT_IN_VA_END:
284 on_va_end (sm_ctxt, node, call);
291 /* Get the svalue for which va_list_state_machine holds state on argument ARG_
294 static const svalue *
295 get_stateful_arg (sm_context *sm_ctxt, const gcall *call, unsigned arg_idx)
297 tree ap = gimple_call_arg (call, arg_idx);
299 && POINTER_TYPE_P (TREE_TYPE (ap)))
301 if (const program_state *new_state = sm_ctxt->get_new_program_state ())
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 ())
315 /* Abstract class for diagnostics relating to va_list_state_machine. */
317 class va_list_sm_diagnostic : public pending_diagnostic
320 bool subclass_equal_p (const pending_diagnostic &base_other) const override
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));
328 label_text describe_state_change (const evdesc::state_change &change)
331 if (const char *fnname = maybe_get_fnname (change))
332 return change.formatted_print ("%qs called here", fnname);
333 return label_text ();
336 diagnostic_event::meaning
337 get_meaning_for_state_change (const evdesc::state_change &change)
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 ();
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)
355 static const char *maybe_get_fnname (const evdesc::state_change &change)
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))
361 if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL))
362 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
364 case BUILT_IN_VA_START:
366 case BUILT_IN_VA_COPY:
368 case BUILT_IN_VA_END:
375 const va_list_state_machine &m_sm;
376 const svalue *m_ap_sval;
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. */
383 class va_list_use_after_va_end : public va_list_sm_diagnostic
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)
394 int get_controlling_option () const final override
396 return OPT_Wanalyzer_va_list_use_after_va_end;
399 bool operator== (const va_list_use_after_va_end &other) const
401 return (va_list_sm_diagnostic::subclass_equal_p (other)
402 && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
405 bool emit (rich_location *rich_loc) final override
407 auto_diagnostic_group d;
408 return warning_at (rich_loc, get_controlling_option (),
409 "%qs after %qs", m_usage_fnname, "va_end");
412 const char *get_kind () const final override
414 return "va_list_use_after_va_end";
417 label_text describe_state_change (const evdesc::state_change &change)
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);
425 label_text describe_final_event (const evdesc::final_event &ev) final override
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);
434 return ev.formatted_print
435 ("%qs on %qE after %qs",
436 m_usage_fnname, ev.m_expr, "va_end");
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);
445 return ev.formatted_print
447 m_usage_fnname, "va_end");
452 diagnostic_event_id_t m_va_end_event;
453 const char *m_usage_fnname;
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. */
460 class va_list_leak : public va_list_sm_diagnostic
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)
470 int get_controlling_option () const final override
472 return OPT_Wanalyzer_va_list_leak;
475 bool operator== (const va_list_leak &other) const
477 return va_list_sm_diagnostic::subclass_equal_p (other);
480 bool emit (rich_location *rich_loc) final override
482 auto_diagnostic_group d;
483 return warning_at (rich_loc, get_controlling_option (),
484 "missing call to %qs", "va_end");
487 const char *get_kind () const final override { return "va_list_leak"; }
489 label_text describe_state_change (const evdesc::state_change &change)
492 if (change.m_new_state == m_sm.m_started)
494 m_start_event = change.m_event_id;
495 m_start_event_fnname = maybe_get_fnname (change);
497 return va_list_sm_diagnostic::describe_state_change (change);
500 label_text describe_final_event (const evdesc::final_event &ev) final override
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);
509 return ev.formatted_print
510 ("missing call to %qs on %qE",
511 "va_end", ev.m_expr);
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);
520 return ev.formatted_print
521 ("missing call to %qs",
527 diagnostic_event_id_t m_start_event;
528 const char *m_start_event_fnname;
531 /* Update state machine for a "va_start" call. */
534 va_list_state_machine::on_va_start (sm_context *sm_ctxt,
536 const gcall *call) const
538 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
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);
547 /* Complain if ARG is in the "ended" state. */
550 va_list_state_machine::check_for_ended_va_list (sm_context *sm_ctxt,
551 const supernode *node,
554 const char *usage_fnname) const
556 if (sm_ctxt->get_state (call, arg) == m_ended)
557 sm_ctxt->warn (node, call, arg,
558 make_unique<va_list_use_after_va_end>
559 (*this, arg, NULL_TREE, usage_fnname));
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,
564 or NULL otherwise. */
566 static const svalue *
567 get_stateful_va_copy_arg (sm_context *sm_ctxt,
571 if (const program_state *new_state = sm_ctxt->get_new_program_state ())
573 const region_model *new_model = new_state->m_region_model;
574 const svalue *arg = get_va_copy_arg (new_model, NULL, call, arg_idx);
580 /* Update state machine for a "va_copy" call. */
583 va_list_state_machine::on_va_copy (sm_context *sm_ctxt,
584 const supernode *node,
585 const gcall *call) const
587 const svalue *src_arg = get_stateful_va_copy_arg (sm_ctxt, call, 1);
589 check_for_ended_va_list (sm_ctxt, node, call, src_arg, "va_copy");
591 const svalue *dst_arg = get_stateful_arg (sm_ctxt, call, 0);
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);
600 /* Update state machine for a "va_arg" call. */
603 va_list_state_machine::on_va_arg (sm_context *sm_ctxt,
604 const supernode *node,
605 const gcall *call) const
607 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
609 check_for_ended_va_list (sm_ctxt, node, call, arg, "va_arg");
612 /* Update state machine for a "va_end" call. */
615 va_list_state_machine::on_va_end (sm_context *sm_ctxt,
616 const supernode *node,
617 const gcall *call) const
619 const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
622 state_t s = sm_ctxt->get_state (call, arg);
623 /* Transition from "started" to "ended". */
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");
631 /* Implementation of state_machine::on_leak vfunc for va_list_state_machine
632 (for complaining about leaks of values in state 'started'). */
634 std::unique_ptr<pending_diagnostic>
635 va_list_state_machine::on_leak (tree var) const
637 return make_unique<va_list_leak> (*this, NULL, var);
640 } // anonymous namespace
642 /* Internal interface to this file. */
645 make_va_list_state_machine (logger *logger)
647 return new va_list_state_machine (logger);
650 /* Handler for "__builtin_va_start". */
652 class kf_va_start : public known_function
655 bool matches_call_types_p (const call_details &) const final override
659 void impl_call_pre (const call_details &cd) const final override;
663 kf_va_start::impl_call_pre (const call_details &cd) const
665 region_model *model = cd.get_model ();
666 region_model_manager *mgr = cd.get_manager ();
667 const svalue *out_ptr = cd.get_arg_svalue (0);
668 const region *out_reg
669 = model->deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
670 const frame_region *frame = model->get_current_frame ();
672 /* "*out_ptr = &IMPL_REGION;". */
673 const region *impl_reg = mgr->create_region_for_alloca (frame);
675 /* We abuse the types here, since va_list_type isn't
676 necessarily anything to do with a pointer. */
677 const svalue *ptr_to_impl_reg = mgr->get_ptr_svalue (NULL_TREE, impl_reg);
678 model->set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ());
680 if (model->get_stack_depth () > 1)
682 /* The interprocedural case: the frame containing the va_start call
683 will have been populated with any variadic aruguments.
684 Initialize IMPL_REGION with a ptr to var_arg_region 0. */
685 const region *init_var_arg_reg = mgr->get_var_arg_region (frame, 0);
686 const svalue *ap_sval
687 = mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg);
688 model->set_value (impl_reg, ap_sval, cd.get_ctxt ());
692 /* The frame containing va_start is an entry-point to the analysis,
693 so there won't be any specific var_arg_regions populated within it.
694 Initialize IMPL_REGION as the UNKNOWN_SVALUE to avoid state
695 explosions on repeated calls to va_arg. */
696 const svalue *unknown_sval
697 = mgr->get_or_create_unknown_svalue (NULL_TREE);
698 model->set_value (impl_reg, unknown_sval, cd.get_ctxt ());
702 /* Handler for "__builtin_va_copy". */
704 class kf_va_copy : public known_function
707 bool matches_call_types_p (const call_details &) const final override
711 void impl_call_pre (const call_details &cd) const final override;
715 kf_va_copy::impl_call_pre (const call_details &cd) const
717 region_model *model = cd.get_model ();
718 region_model_manager *mgr = cd.get_manager ();
719 const svalue *out_dst_ptr = cd.get_arg_svalue (0);
720 const svalue *in_va_list
721 = get_va_copy_arg (model, cd.get_ctxt (), cd.get_call_stmt (), 1);
723 = model->check_for_poison (in_va_list,
724 get_va_list_diag_arg (cd.get_arg_tree (1)),
727 const region *out_dst_reg
728 = model->deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
730 /* "*out_dst_ptr = &NEW_IMPL_REGION;". */
731 const region *new_impl_reg
732 = mgr->create_region_for_alloca (model->get_current_frame ());
733 const svalue *ptr_to_new_impl_reg
734 = mgr->get_ptr_svalue (NULL_TREE, new_impl_reg);
735 model->set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ());
737 if (const region *old_impl_reg = in_va_list->maybe_get_region ())
739 /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);". */
740 const svalue *existing_sval
741 = model->get_store_value (old_impl_reg, cd.get_ctxt ());
742 model->set_value (new_impl_reg, existing_sval, cd.get_ctxt ());
746 /* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT. */
749 get_num_variadic_arguments (tree callee_fndecl,
750 const gcall *call_stmt)
752 int num_positional = 0;
753 for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
754 iter_parm = DECL_CHAIN (iter_parm))
756 return gimple_call_num_args (call_stmt) - num_positional;
759 /* An abstract subclass of pending_diagnostic for diagnostics relating
760 to bad va_arg invocations.
762 This shows the number of variadic arguments at the call of interest.
763 Ideally we'd also be able to highlight individual arguments, but
764 that location information isn't generally available from the middle end. */
766 class va_arg_diagnostic : public pending_diagnostic
769 /* Override of pending_diagnostic::add_call_event,
770 adding a custom call_event subclass. */
771 void add_call_event (const exploded_edge &eedge,
772 checker_path *emission_path) override
774 /* As per call_event, but show the number of variadic arguments
776 class va_arg_call_event : public call_event
779 va_arg_call_event (const exploded_edge &eedge,
780 location_t loc, tree fndecl, int depth,
781 int num_variadic_arguments)
782 : call_event (eedge, loc, fndecl, depth),
783 m_num_variadic_arguments (num_variadic_arguments)
787 label_text get_desc (bool can_colorize) const override
789 return make_label_text_n
790 (can_colorize, m_num_variadic_arguments,
791 "calling %qE from %qE with %i variadic argument",
792 "calling %qE from %qE with %i variadic arguments",
793 get_callee_fndecl (),
794 get_caller_fndecl (),
795 m_num_variadic_arguments);
798 int m_num_variadic_arguments;
801 const frame_region *frame_reg = m_var_arg_reg->get_frame_region ();
802 const exploded_node *dst_node = eedge.m_dest;
803 if (dst_node->get_state ().m_region_model->get_current_frame ()
806 const exploded_node *src_node = eedge.m_src;
807 const program_point &src_point = src_node->get_point ();
808 const int src_stack_depth = src_point.get_stack_depth ();
809 const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
810 const gcall *call_stmt = as_a <const gcall *> (last_stmt);
811 int num_variadic_arguments
812 = get_num_variadic_arguments (dst_node->get_function ()->decl,
814 emission_path->add_event
815 (make_unique<va_arg_call_event> (eedge,
817 ? last_stmt->location
819 src_point.get_fndecl (),
821 num_variadic_arguments));
824 pending_diagnostic::add_call_event (eedge, emission_path);
828 va_arg_diagnostic (tree va_list_tree, const var_arg_region *var_arg_reg)
829 : m_va_list_tree (va_list_tree), m_var_arg_reg (var_arg_reg)
832 bool subclass_equal_p (const pending_diagnostic &base_other) const override
834 const va_arg_diagnostic &other = (const va_arg_diagnostic &)base_other;
835 return (same_tree_p (m_va_list_tree, other.m_va_list_tree)
836 && m_var_arg_reg == other.m_var_arg_reg);
839 /* Get the number of arguments consumed so far from the va_list
840 (*before* this va_arg call). */
841 unsigned get_num_consumed () const
843 return m_var_arg_reg->get_index ();
846 /* Get a 1-based index of which variadic argument is being consumed. */
847 unsigned get_variadic_index_for_diagnostic () const
849 return get_num_consumed () + 1;
852 /* User-readable expr for the va_list argument to va_arg. */
855 /* The region that the va_arg attempted to access. */
856 const var_arg_region *m_var_arg_reg;
859 /* A subclass of pending_diagnostic for complaining about a type mismatch
860 between the result of:
862 and the type of the argument that was passed to the variadic call. */
864 class va_arg_type_mismatch : public va_arg_diagnostic
867 va_arg_type_mismatch (tree va_list_tree, const var_arg_region *var_arg_reg,
868 tree expected_type, tree actual_type)
869 : va_arg_diagnostic (va_list_tree, var_arg_reg),
870 m_expected_type (expected_type), m_actual_type (actual_type)
873 const char *get_kind () const final override
875 return "va_arg_type_mismatch";
878 bool subclass_equal_p (const pending_diagnostic &base_other)
881 if (!va_arg_diagnostic::subclass_equal_p (base_other))
883 const va_arg_type_mismatch &other
884 = (const va_arg_type_mismatch &)base_other;
885 return (same_tree_p (m_expected_type, other.m_expected_type)
886 && same_tree_p (m_actual_type, other.m_actual_type));
889 int get_controlling_option () const final override
891 return OPT_Wanalyzer_va_arg_type_mismatch;
894 bool emit (rich_location *rich_loc) final override
896 auto_diagnostic_group d;
897 diagnostic_metadata m;
898 /* "CWE-686: Function Call With Incorrect Argument Type". */
901 = warning_meta (rich_loc, m, get_controlling_option (),
902 "%<va_arg%> expected %qT but received %qT"
903 " for variadic argument %i of %qE",
904 m_expected_type, m_actual_type,
905 get_variadic_index_for_diagnostic (), m_va_list_tree);
909 label_text describe_final_event (const evdesc::final_event &ev) final override
911 return ev.formatted_print ("%<va_arg%> expected %qT but received %qT"
912 " for variadic argument %i of %qE",
913 m_expected_type, m_actual_type,
914 get_variadic_index_for_diagnostic (),
919 tree m_expected_type;
923 /* A subclass of pending_diagnostic for complaining about a
925 after all of the args in AP have been consumed. */
927 class va_list_exhausted : public va_arg_diagnostic
930 va_list_exhausted (tree va_list_tree, const var_arg_region *var_arg_reg)
931 : va_arg_diagnostic (va_list_tree, var_arg_reg)
934 const char *get_kind () const final override
936 return "va_list_exhausted";
939 int get_controlling_option () const final override
941 return OPT_Wanalyzer_va_list_exhausted;
944 bool emit (rich_location *rich_loc) final override
946 auto_diagnostic_group d;
947 diagnostic_metadata m;
948 /* CWE-685: Function Call With Incorrect Number of Arguments. */
950 bool warned = warning_meta (rich_loc, m, get_controlling_option (),
951 "%qE has no more arguments (%i consumed)",
952 m_va_list_tree, get_num_consumed ());
956 label_text describe_final_event (const evdesc::final_event &ev) final override
958 return ev.formatted_print ("%qE has no more arguments (%i consumed)",
959 m_va_list_tree, get_num_consumed ());
963 /* Return true if it's OK to copy a value from ARG_TYPE to LHS_TYPE via
964 va_arg (where argument promotion has already happened). */
967 va_arg_compatible_types_p (tree lhs_type, tree arg_type)
969 return compat_types_p (arg_type, lhs_type);
972 /* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
973 Otherwise return NULL. */
975 static const var_arg_region *
976 maybe_get_var_arg_region (const svalue *ap_sval)
978 if (const region *reg = ap_sval->maybe_get_region ())
979 return reg->dyn_cast_var_arg_region ();
983 /* Handler for "__builtin_va_arg". */
985 class kf_va_arg : public internal_known_function
988 void impl_call_pre (const call_details &cd) const final override;
992 kf_va_arg::impl_call_pre (const call_details &cd) const
994 region_model_context *ctxt = cd.get_ctxt ();
995 region_model *model = cd.get_model ();
996 region_model_manager *mgr = cd.get_manager ();
998 const svalue *in_ptr = cd.get_arg_svalue (0);
1000 = model->deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt);
1002 const svalue *ap_sval = model->get_store_value (ap_reg, ctxt);
1003 if (const svalue *cast = ap_sval->maybe_undo_cast ())
1006 tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0));
1007 ap_sval = model->check_for_poison (ap_sval, va_list_tree, ctxt);
1009 if (const region *impl_reg = ap_sval->maybe_get_region ())
1011 const svalue *old_impl_sval = model->get_store_value (impl_reg, ctxt);
1012 if (const var_arg_region *arg_reg
1013 = maybe_get_var_arg_region (old_impl_sval))
1015 bool saw_problem = false;
1017 const frame_region *frame_reg = arg_reg->get_frame_region ();
1018 unsigned next_arg_idx = arg_reg->get_index ();
1020 if (frame_reg->get_stack_depth () > 1)
1022 /* The interprocedural case: the called frame will have been
1023 populated with any variadic aruguments.
1024 Attempt to extract arg_reg to cd's return region (which already
1025 has a conjured_svalue), or warn if there's a problem
1026 (incompatible types, or if we've run out of args). */
1027 if (const svalue *arg_sval
1028 = model->get_store ()->get_any_binding
1029 (mgr->get_store_manager (), arg_reg))
1031 tree lhs_type = cd.get_lhs_type ();
1032 tree arg_type = arg_sval->get_type ();
1033 if (va_arg_compatible_types_p (lhs_type, arg_type))
1034 cd.maybe_set_lhs (arg_sval);
1038 ctxt->warn (make_unique <va_arg_type_mismatch>
1049 ctxt->warn (make_unique <va_list_exhausted> (va_list_tree,
1056 /* This frame is an entry-point to the analysis, so there won't be
1057 any specific var_arg_regions populated within it.
1058 We already have a conjured_svalue for the result, so leave
1060 gcc_assert (frame_reg->get_stack_depth () == 1);
1065 /* Set impl_reg to UNKNOWN to suppress further warnings. */
1066 const svalue *new_ap_sval
1067 = mgr->get_or_create_unknown_svalue (impl_reg->get_type ());
1068 model->set_value (impl_reg, new_ap_sval, ctxt);
1072 /* Update impl_reg to advance to the next arg. */
1073 const region *next_var_arg_region
1074 = mgr->get_var_arg_region (frame_reg, next_arg_idx + 1);
1075 const svalue *new_ap_sval
1076 = mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region);
1077 model->set_value (impl_reg, new_ap_sval, ctxt);
1083 /* Handler for "__builtin_va_end". */
1085 class kf_va_end : public known_function
1088 bool matches_call_types_p (const call_details &) const
1094 /* Populate KFM with instances of known functions relating to varargs. */
1097 register_varargs_builtins (known_function_manager &kfm)
1099 kfm.add (BUILT_IN_VA_START, make_unique<kf_va_start> ());
1100 kfm.add (BUILT_IN_VA_COPY, make_unique<kf_va_copy> ());
1101 kfm.add (IFN_VA_ARG, make_unique<kf_va_arg> ());
1102 kfm.add (BUILT_IN_VA_END, make_unique<kf_va_end> ());
1107 #endif /* #if ENABLE_ANALYZER */