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/>. */
23 #include "coretypes.h"
26 #include "basic-block.h"
28 #include "diagnostic-path.h"
29 #include "analyzer/analyzer.h"
30 #include "analyzer/analyzer-logging.h"
31 #include "analyzer/sm.h"
32 #include "analyzer/pending-diagnostic.h"
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"
39 #include "analyzer/supergraph.h"
40 #include "analyzer/diagnostic-manager.h"
41 #include "analyzer/exploded-graph.h"
42 #include "diagnostic-metadata.h"
48 /* Implementation of <stdarg.h> within analyzer.
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
59 The analyzer runs *before* the "stdarg" and "lower_vaarg" gimple
60 passes, which have target-dependent effects.
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.
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
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.
84 'impl_reg': pointer to next var_arg_region
85 var_arg_region for arg 0
87 var_arg_region for arg N-1
89 Hence given test_1 in stdarg-1.c, at the call to:
91 __analyzer_called_by_test_1 (int placeholder, ...);
95 __analyzer_called_by_test_1 (42, "foo", 1066, '@');
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
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)
120 0x4c83700: &ALLOCA_REGION: started
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.
128 At the va_end, we can set &ALLOCA_REGION's state to "ended".
130 The various __builtin_va_* accept ap by pointer, so we have e.g.:
132 __builtin_va_start (&ap, [...]);
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
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. */
143 get_va_list_diag_arg (tree va_list_tree
)
145 if (TREE_CODE (va_list_tree
) == ADDR_EXPR
)
146 va_list_tree
= TREE_OPERAND (va_list_tree
, 0);
150 /* Get argument ARG_IDX of type BT_VALIST_ARG (for use by va_copy).
152 builtin-types.def has:
153 DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
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. */
159 static const svalue
*
160 get_BT_VALIST_ARG (const region_model
*model
,
161 region_model_context
*ctxt
,
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 ())
169 if (TREE_CODE (va_list_type_node
) == ARRAY_TYPE
)
171 /* va_list_arg_type_node is a pointer to a va_list;
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 ())
181 /* va_list_arg_type_node is a va_list; return ARG_SVAL. */
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. */
194 class va_list_state_machine
: public state_machine
197 va_list_state_machine (logger
*logger
);
199 bool inherited_state_p () const final override
{ return false; }
201 bool on_stmt (sm_context
*sm_ctxt
,
202 const supernode
*node
,
203 const gimple
*stmt
) const final override
;
205 bool can_purge_p (state_t s
) const final override
207 return s
!= m_started
;
209 pending_diagnostic
*on_leak (tree var
) const final override
;
211 /* State for a va_list that the result of a va_start or va_copy. */
214 /* State for a va_list that has had va_end called on it. */
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
,
230 const char *usage_fnname
) const;
233 /* va_list_state_machine's ctor. */
235 va_list_state_machine::va_list_state_machine (logger
*logger
)
236 : state_machine ("va_list", logger
)
238 m_started
= add_state ("started");
239 m_ended
= add_state ("ended");
242 /* Implementation of the various "va_*" functions for
243 va_list_state_machine. */
246 va_list_state_machine::on_stmt (sm_context
*sm_ctxt
,
247 const supernode
*node
,
248 const gimple
*stmt
) const
250 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
252 if (gimple_call_internal_p (call
)
253 && gimple_call_internal_fn (call
) == IFN_VA_ARG
)
255 on_va_arg (sm_ctxt
, node
, call
);
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
))
267 case BUILT_IN_VA_START
:
268 on_va_start (sm_ctxt
, node
, call
);
271 case BUILT_IN_VA_COPY
:
272 on_va_copy (sm_ctxt
, node
, call
);
275 case BUILT_IN_VA_END
:
276 on_va_end (sm_ctxt
, node
, call
);
283 /* Get the svalue for which va_list_state_machine holds state on argument ARG_
286 static const svalue
*
287 get_stateful_arg (sm_context
*sm_ctxt
, const gcall
*call
, unsigned arg_idx
)
289 tree ap
= gimple_call_arg (call
, arg_idx
);
291 && POINTER_TYPE_P (TREE_TYPE (ap
)))
293 if (const program_state
*new_state
= sm_ctxt
->get_new_program_state ())
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 ())
307 /* Abstract class for diagnostics relating to va_list_state_machine. */
309 class va_list_sm_diagnostic
: public pending_diagnostic
312 bool subclass_equal_p (const pending_diagnostic
&base_other
) const override
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
));
320 label_text
describe_state_change (const evdesc::state_change
&change
)
323 if (const char *fnname
= maybe_get_fnname (change
))
324 return change
.formatted_print ("%qs called here", fnname
);
325 return label_text ();
328 diagnostic_event::meaning
329 get_meaning_for_state_change (const evdesc::state_change
&change
)
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 ();
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
)
347 static const char *maybe_get_fnname (const evdesc::state_change
&change
)
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
))
353 if (fndecl_built_in_p (callee_fndecl
, BUILT_IN_NORMAL
))
354 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl
))
356 case BUILT_IN_VA_START
:
358 case BUILT_IN_VA_COPY
:
360 case BUILT_IN_VA_END
:
367 const va_list_state_machine
&m_sm
;
368 const svalue
*m_ap_sval
;
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. */
375 class va_list_use_after_va_end
: public va_list_sm_diagnostic
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
)
386 int get_controlling_option () const final override
388 return OPT_Wanalyzer_va_list_use_after_va_end
;
391 bool operator== (const va_list_use_after_va_end
&other
) const
393 return (va_list_sm_diagnostic::subclass_equal_p (other
)
394 && 0 == strcmp (m_usage_fnname
, other
.m_usage_fnname
));
397 bool emit (rich_location
*rich_loc
) final override
399 auto_diagnostic_group d
;
400 return warning_at (rich_loc
, get_controlling_option (),
401 "%qs after %qs", m_usage_fnname
, "va_end");
404 const char *get_kind () const final override
406 return "va_list_use_after_va_end";
409 label_text
describe_state_change (const evdesc::state_change
&change
)
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
);
417 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
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
);
426 return ev
.formatted_print
427 ("%qs on %qE after %qs",
428 m_usage_fnname
, ev
.m_expr
, "va_end");
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
);
437 return ev
.formatted_print
439 m_usage_fnname
, "va_end");
444 diagnostic_event_id_t m_va_end_event
;
445 const char *m_usage_fnname
;
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. */
452 class va_list_leak
: public va_list_sm_diagnostic
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
)
462 int get_controlling_option () const final override
464 return OPT_Wanalyzer_va_list_leak
;
467 bool operator== (const va_list_leak
&other
) const
469 return va_list_sm_diagnostic::subclass_equal_p (other
);
472 bool emit (rich_location
*rich_loc
) final override
474 auto_diagnostic_group d
;
475 return warning_at (rich_loc
, get_controlling_option (),
476 "missing call to %qs", "va_end");
479 const char *get_kind () const final override
{ return "va_list_leak"; }
481 label_text
describe_state_change (const evdesc::state_change
&change
)
484 if (change
.m_new_state
== m_sm
.m_started
)
486 m_start_event
= change
.m_event_id
;
487 m_start_event_fnname
= maybe_get_fnname (change
);
489 return va_list_sm_diagnostic::describe_state_change (change
);
492 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
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
);
501 return ev
.formatted_print
502 ("missing call to %qs on %qE",
503 "va_end", ev
.m_expr
);
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
);
512 return ev
.formatted_print
513 ("missing call to %qs",
519 diagnostic_event_id_t m_start_event
;
520 const char *m_start_event_fnname
;
523 /* Update state machine for a "va_start" call. */
526 va_list_state_machine::on_va_start (sm_context
*sm_ctxt
,
528 const gcall
*call
) const
530 const svalue
*arg
= get_stateful_arg (sm_ctxt
, call
, 0);
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
);
539 /* Complain if ARG is in the "ended" state. */
542 va_list_state_machine::check_for_ended_va_list (sm_context
*sm_ctxt
,
543 const supernode
*node
,
546 const char *usage_fnname
) const
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
,
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. */
558 static const svalue
*
559 get_stateful_BT_VALIST_ARG (sm_context
*sm_ctxt
,
563 if (const program_state
*new_state
= sm_ctxt
->get_new_program_state ())
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
);
572 /* Update state machine for a "va_copy" call. */
575 va_list_state_machine::on_va_copy (sm_context
*sm_ctxt
,
576 const supernode
*node
,
577 const gcall
*call
) const
579 const svalue
*src_arg
= get_stateful_BT_VALIST_ARG (sm_ctxt
, call
, 1);
581 check_for_ended_va_list (sm_ctxt
, node
, call
, src_arg
, "va_copy");
583 const svalue
*dst_arg
= get_stateful_arg (sm_ctxt
, call
, 0);
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
);
592 /* Update state machine for a "va_arg" call. */
595 va_list_state_machine::on_va_arg (sm_context
*sm_ctxt
,
596 const supernode
*node
,
597 const gcall
*call
) const
599 const svalue
*arg
= get_stateful_arg (sm_ctxt
, call
, 0);
601 check_for_ended_va_list (sm_ctxt
, node
, call
, arg
, "va_arg");
604 /* Update state machine for a "va_end" call. */
607 va_list_state_machine::on_va_end (sm_context
*sm_ctxt
,
608 const supernode
*node
,
609 const gcall
*call
) const
611 const svalue
*arg
= get_stateful_arg (sm_ctxt
, call
, 0);
614 state_t s
= sm_ctxt
->get_state (call
, arg
);
615 /* Transition from "started" to "ended". */
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");
623 /* Implementation of state_machine::on_leak vfunc for va_list_state_machine
624 (for complaining about leaks of values in state 'started'). */
627 va_list_state_machine::on_leak (tree var
) const
629 return new va_list_leak (*this, NULL
, var
);
632 } // anonymous namespace
634 /* Internal interface to this file. */
637 make_va_list_state_machine (logger
*logger
)
639 return new va_list_state_machine (logger
);
642 /* Handle the on_call_pre part of "__builtin_va_start". */
645 region_model::impl_call_va_start (const call_details
&cd
)
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 ());
651 /* "*out_ptr = &IMPL_REGION;". */
652 const region
*impl_reg
= m_mgr
->create_region_for_alloca (m_current_frame
);
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 ());
659 if (get_stack_depth () > 1)
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 ());
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 ());
682 /* Handle the on_call_pre part of "__builtin_va_copy". */
685 region_model::impl_call_va_copy (const call_details
&cd
)
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)),
694 const region
*out_dst_reg
695 = deref_rvalue (out_dst_ptr
, cd
.get_arg_tree (0), cd
.get_ctxt ());
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 ());
704 if (const region
*old_impl_reg
= in_va_list
->maybe_get_region ())
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 ());
714 /* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT. */
717 get_num_variadic_arguments (tree callee_fndecl
,
718 const gcall
*call_stmt
)
720 int num_positional
= 0;
721 for (tree iter_parm
= DECL_ARGUMENTS (callee_fndecl
); iter_parm
;
722 iter_parm
= DECL_CHAIN (iter_parm
))
724 return gimple_call_num_args (call_stmt
) - num_positional
;
727 /* An abstract subclass of pending_diagnostic for diagnostics relating
728 to bad va_arg invocations.
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. */
734 class va_arg_diagnostic
: public pending_diagnostic
737 /* Override of pending_diagnostic::add_call_event,
738 adding a custom call_event subclass. */
739 void add_call_event (const exploded_edge
&eedge
,
740 checker_path
*emission_path
) override
742 /* As per call_event, but show the number of variadic arguments
744 class va_arg_call_event
: public call_event
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
)
755 label_text
get_desc (bool can_colorize
) const override
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
);
766 int m_num_variadic_arguments
;
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 ()
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
,
782 emission_path
->add_event
783 (new va_arg_call_event (eedge
,
785 ? last_stmt
->location
787 src_point
.get_fndecl (),
789 num_variadic_arguments
));
792 pending_diagnostic::add_call_event (eedge
, emission_path
);
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
)
800 bool subclass_equal_p (const pending_diagnostic
&base_other
) const override
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
);
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
811 return m_var_arg_reg
->get_index ();
814 /* Get a 1-based index of which variadic argument is being consumed. */
815 unsigned get_variadic_index_for_diagnostic () const
817 return get_num_consumed () + 1;
820 /* User-readable expr for the va_list argument to va_arg. */
823 /* The region that the va_arg attempted to access. */
824 const var_arg_region
*m_var_arg_reg
;
827 /* A subclass of pending_diagnostic for complaining about a type mismatch
828 between the result of:
830 and the type of the argument that was passed to the variadic call. */
832 class va_arg_type_mismatch
: public va_arg_diagnostic
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
)
841 const char *get_kind () const final override
843 return "va_arg_type_mismatch";
846 bool subclass_equal_p (const pending_diagnostic
&base_other
)
849 if (!va_arg_diagnostic::subclass_equal_p (base_other
))
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
));
857 int get_controlling_option () const final override
859 return OPT_Wanalyzer_va_arg_type_mismatch
;
862 bool emit (rich_location
*rich_loc
) final override
864 auto_diagnostic_group d
;
865 diagnostic_metadata m
;
866 /* "CWE-686: Function Call With Incorrect Argument Type". */
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
);
877 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
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 (),
887 tree m_expected_type
;
891 /* A subclass of pending_diagnostic for complaining about a
893 after all of the args in AP have been consumed. */
895 class va_list_exhausted
: public va_arg_diagnostic
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
)
902 const char *get_kind () const final override
904 return "va_list_exhausted";
907 int get_controlling_option () const final override
909 return OPT_Wanalyzer_va_list_exhausted
;
912 bool emit (rich_location
*rich_loc
) final override
914 auto_diagnostic_group d
;
915 diagnostic_metadata m
;
916 /* CWE-685: Function Call With Incorrect Number of Arguments. */
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 ());
924 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
926 return ev
.formatted_print ("%qE has no more arguments (%i consumed)",
927 m_va_list_tree
, get_num_consumed ());
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). */
935 va_arg_compatible_types_p (tree lhs_type
, tree arg_type
)
937 return compat_types_p (arg_type
, lhs_type
);
940 /* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
941 Otherwise return NULL. */
943 static const var_arg_region
*
944 maybe_get_var_arg_region (const svalue
*ap_sval
)
946 if (const region
*reg
= ap_sval
->maybe_get_region ())
947 return reg
->dyn_cast_var_arg_region ();
951 /* Handle the on_call_pre part of "__builtin_va_arg". */
954 region_model::impl_call_va_arg (const call_details
&cd
)
956 region_model_context
*ctxt
= cd
.get_ctxt ();
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
);
961 const svalue
*ap_sval
= get_store_value (ap_reg
, ctxt
);
962 if (const svalue
*cast
= ap_sval
->maybe_undo_cast ())
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
);
968 if (const region
*impl_reg
= ap_sval
->maybe_get_region ())
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
))
974 bool saw_problem
= false;
976 const frame_region
*frame_reg
= arg_reg
->get_frame_region ();
977 unsigned next_arg_idx
= arg_reg
->get_index ();
979 if (frame_reg
->get_stack_depth () > 1)
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 (),
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
);
997 ctxt
->warn (new va_arg_type_mismatch (va_list_tree
,
1007 ctxt
->warn (new va_list_exhausted (va_list_tree
, arg_reg
));
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
1017 gcc_assert (frame_reg
->get_stack_depth () == 1);
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
);
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
);
1040 /* Handle the on_call_post part of "__builtin_va_end". */
1043 region_model::impl_call_va_end (const call_details
&)
1050 #endif /* #if ENABLE_ANALYZER */