1 /* Implementation of <stdarg.h> within analyzer.
2 Copyright (C) 2022-2024 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
23 #define INCLUDE_VECTOR
25 #include "coretypes.h"
26 #include "make-unique.h"
29 #include "basic-block.h"
31 #include "diagnostic-path.h"
32 #include "analyzer/analyzer.h"
33 #include "analyzer/analyzer-logging.h"
34 #include "analyzer/sm.h"
35 #include "analyzer/pending-diagnostic.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 "analyzer/supergraph.h"
43 #include "analyzer/diagnostic-manager.h"
44 #include "analyzer/exploded-graph.h"
45 #include "analyzer/call-details.h"
51 /* Implementation of <stdarg.h> within analyzer.
54 - detection of interprocedural type errors involving va_arg
55 - tracking of symbolic values interprocedurally from variadic call
56 through to va_arg unpacking
57 - detection of missing va_end
58 - detection of va_arg outside of a va_start/va_end pair
59 - detection of uses of a va_list after the frame in containing the
62 The analyzer runs *before* the "stdarg" and "lower_vaarg" gimple
63 passes, which have target-dependent effects.
65 This file implements a state machine on svalues for tracking when
66 va_start has been called, so that we can detect missing va_end,
67 and misplaced va_arg, etc.
68 To do this requires an svalue that can have state, so we implement va_start
69 by creating a stack-allocated region, and use a pointer to that region
70 as the svalue that has state.
72 We call this stack-allocated region the "impl_reg". Allocating it on
73 the stack ensures that it is invalidated when the frame containing
74 the va_start returns, leading to
75 -Wanalyzer-use-of-pointer-in-stale-stack-frame on attempts to use such
78 To track svalues from variadic calls interprocedurally, we implement
79 variadic arguments via new child regions of the callee's frame_region,
80 var_arg_region, each one representing a storage slot for one of the
81 variadic arguments, accessed by index.
87 'impl_reg': pointer to next var_arg_region
88 var_arg_region for arg 0
90 var_arg_region for arg N-1
92 Hence given test_1 in stdarg-1.c, at the call to:
94 __analyzer_called_by_test_1 (int placeholder, ...);
98 __analyzer_called_by_test_1 (42, "foo", 1066, '@');
100 we push this frame for the called function:
101 clusters within frame: ‘__analyzer_called_by_test_1’@2
102 cluster for: placeholder: (int)42
103 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0): &"foo" (TOUCHED)
104 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 1): (int)1066 (TOUCHED)
105 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 2): (int)64 (TOUCHED)
106 where the called function's frame has been populated with both the value
107 of the regular argument "placeholder", and with values for 3 variadic
111 va_start (ap, placeholder);
112 we allocate a region ALLOCA_REGION for ap to point to, populate that
113 region with the address of variadic argument 0, and set sm-state of
114 &ALLOCA_REGION to "started":
115 clusters within frame: ‘__analyzer_called_by_test_1’@2
116 cluster for: placeholder: (int)42
117 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0): &"foo" (TOUCHED)
118 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 1): (int)1066 (TOUCHED)
119 cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 2): (int)64 (TOUCHED)
120 cluster for: ap: &ALLOCA_REGION
121 cluster for: ALLOCA_REGION: &VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0) (TOUCHED)
123 0x4c83700: &ALLOCA_REGION: started
127 we can look within *ap, locate the region holding the next variadic
128 argument to be extracted, extract the svalue, and advance the index
129 by effectively updating *ap.
131 At the va_end, we can set &ALLOCA_REGION's state to "ended".
133 The various __builtin_va_* accept ap by pointer, so we have e.g.:
135 __builtin_va_start (&ap, [...]);
137 except for the 2nd param of __builtin_va_copy, where the type
138 is already target-dependent (see the discussion of get_va_copy_arg
141 /* Get a tree for diagnostics.
142 Typically we have "&ap", but it will make more sense to
143 the user as just "ap", so strip off the ADDR_EXPR. */
146 get_va_list_diag_arg (tree va_list_tree
)
148 if (TREE_CODE (va_list_tree
) == ADDR_EXPR
)
149 va_list_tree
= TREE_OPERAND (va_list_tree
, 0);
153 /* Get argument ARG_IDX of va_copy.
155 builtin-types.def has:
156 DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
158 and c_common_nodes_and_builtins initializes va_list_arg_type_node
159 based on whether TREE_CODE (va_list_type_node) is of ARRAY_TYPE or
160 not, giving either one or zero levels of indirection.
162 Alternatively we could be dealing with __builtin_ms_va_copy or
163 __builtin_sysv_va_copy.
165 Handle this by looking at the types of the argument in question. */
167 static const svalue
*
168 get_va_copy_arg (const region_model
*model
,
169 region_model_context
*ctxt
,
173 tree arg
= gimple_call_arg (call
, arg_idx
);
174 const svalue
*arg_sval
= model
->get_rvalue (arg
, ctxt
);
175 if (const svalue
*cast
= arg_sval
->maybe_undo_cast ())
177 if (TREE_CODE (TREE_TYPE (arg
)) == POINTER_TYPE
178 && TREE_CODE (TREE_TYPE (TREE_TYPE (arg
))) == ARRAY_TYPE
)
180 /* va_list_arg_type_node is a pointer to a va_list;
182 const region
*src_reg
= model
->deref_rvalue (arg_sval
, arg
, ctxt
);
183 const svalue
*src_reg_sval
= model
->get_store_value (src_reg
, ctxt
);
184 if (const svalue
*cast
= src_reg_sval
->maybe_undo_cast ())
190 /* va_list_arg_type_node is a va_list; return ARG_SVAL. */
197 /* A state machine for tracking the state of a va_list, so that
198 we can enforce that each va_start is paired with a va_end,
199 and va_arg only happens within a va_start/va_end pair.
200 Specifically, this tracks the state of the &ALLOCA_BUFFER
201 that va_start/va_copy allocate. */
203 class va_list_state_machine
: public state_machine
206 va_list_state_machine (logger
*logger
);
208 bool inherited_state_p () const final override
{ return false; }
210 bool on_stmt (sm_context
&sm_ctxt
,
211 const supernode
*node
,
212 const gimple
*stmt
) const final override
;
214 bool can_purge_p (state_t s
) const final override
216 return s
!= m_started
;
218 std::unique_ptr
<pending_diagnostic
> on_leak (tree var
) const final override
;
220 /* State for a va_list that is the result of a va_start or va_copy. */
223 /* State for a va_list that has had va_end called on it. */
227 void on_va_start (sm_context
&sm_ctxt
, const supernode
*node
,
228 const gcall
*call
) const;
229 void on_va_copy (sm_context
&sm_ctxt
, const supernode
*node
,
230 const gcall
*call
) const;
231 void on_va_arg (sm_context
&sm_ctxt
, const supernode
*node
,
232 const gcall
*call
) const;
233 void on_va_end (sm_context
&sm_ctxt
, const supernode
*node
,
234 const gcall
*call
) const;
235 void check_for_ended_va_list (sm_context
&sm_ctxt
,
236 const supernode
*node
,
239 const char *usage_fnname
) const;
242 /* va_list_state_machine's ctor. */
244 va_list_state_machine::va_list_state_machine (logger
*logger
)
245 : state_machine ("va_list", logger
),
246 m_started (add_state ("started")),
247 m_ended (add_state ("ended"))
251 /* Implementation of the various "va_*" functions for
252 va_list_state_machine. */
255 va_list_state_machine::on_stmt (sm_context
&sm_ctxt
,
256 const supernode
*node
,
257 const gimple
*stmt
) const
259 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
261 if (gimple_call_internal_p (call
)
262 && gimple_call_internal_fn (call
) == IFN_VA_ARG
)
264 on_va_arg (sm_ctxt
, node
, call
);
268 if (tree callee_fndecl
= sm_ctxt
.get_fndecl_for_call (call
))
269 if (fndecl_built_in_p (callee_fndecl
, BUILT_IN_NORMAL
)
270 && gimple_builtin_call_types_compatible_p (call
, callee_fndecl
))
271 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl
))
276 case BUILT_IN_VA_START
:
277 on_va_start (sm_ctxt
, node
, call
);
280 case BUILT_IN_VA_COPY
:
281 on_va_copy (sm_ctxt
, node
, call
);
284 case BUILT_IN_VA_END
:
285 on_va_end (sm_ctxt
, node
, call
);
292 /* Get the svalue for which va_list_state_machine holds state on argument ARG_
295 static const svalue
*
296 get_stateful_arg (sm_context
&sm_ctxt
, const gcall
*call
, unsigned arg_idx
)
298 tree ap
= gimple_call_arg (call
, arg_idx
);
300 && POINTER_TYPE_P (TREE_TYPE (ap
)))
302 if (const program_state
*new_state
= sm_ctxt
.get_new_program_state ())
304 const region_model
*new_model
= new_state
->m_region_model
;
305 const svalue
*ptr_sval
= new_model
->get_rvalue (ap
, NULL
);
306 const region
*reg
= new_model
->deref_rvalue (ptr_sval
, ap
, NULL
);
307 const svalue
*impl_sval
= new_model
->get_store_value (reg
, NULL
);
308 if (const svalue
*cast
= impl_sval
->maybe_undo_cast ())
316 /* Abstract class for diagnostics relating to va_list_state_machine. */
318 class va_list_sm_diagnostic
: public pending_diagnostic
321 bool subclass_equal_p (const pending_diagnostic
&base_other
) const override
323 const va_list_sm_diagnostic
&other
324 = (const va_list_sm_diagnostic
&)base_other
;
325 return (m_ap_sval
== other
.m_ap_sval
326 && same_tree_p (m_ap_tree
, other
.m_ap_tree
));
329 label_text
describe_state_change (const evdesc::state_change
&change
)
332 if (const char *fnname
= maybe_get_fnname (change
))
333 return change
.formatted_print ("%qs called here", fnname
);
334 return label_text ();
337 diagnostic_event::meaning
338 get_meaning_for_state_change (const evdesc::state_change
&change
)
341 if (change
.m_new_state
== m_sm
.m_started
)
342 return diagnostic_event::meaning (diagnostic_event::VERB_acquire
,
343 diagnostic_event::NOUN_resource
);
344 if (change
.m_new_state
== m_sm
.m_ended
)
345 return diagnostic_event::meaning (diagnostic_event::VERB_release
,
346 diagnostic_event::NOUN_resource
);
347 return diagnostic_event::meaning ();
351 va_list_sm_diagnostic (const va_list_state_machine
&sm
,
352 const svalue
*ap_sval
, tree ap_tree
)
353 : m_sm (sm
), m_ap_sval (ap_sval
), m_ap_tree (ap_tree
)
356 static const char *maybe_get_fnname (const evdesc::state_change
&change
)
358 if (change
.m_event
.m_stmt
)
359 if (const gcall
*call
= as_a
<const gcall
*> (change
.m_event
.m_stmt
))
360 if (tree callee_fndecl
= gimple_call_fndecl (call
))
362 if (fndecl_built_in_p (callee_fndecl
, BUILT_IN_NORMAL
))
363 switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl
))
365 case BUILT_IN_VA_START
:
367 case BUILT_IN_VA_COPY
:
369 case BUILT_IN_VA_END
:
376 const va_list_state_machine
&m_sm
;
377 const svalue
*m_ap_sval
;
381 /* Concrete class for -Wanalyzer-va-list-use-after-va-end:
382 complain about use of a va_list after va_end has been called on it. */
384 class va_list_use_after_va_end
: public va_list_sm_diagnostic
387 va_list_use_after_va_end (const va_list_state_machine
&sm
,
388 const svalue
*ap_sval
, tree ap_tree
,
389 const char *usage_fnname
)
390 : va_list_sm_diagnostic (sm
, ap_sval
, ap_tree
),
391 m_usage_fnname (usage_fnname
)
395 int get_controlling_option () const final override
397 return OPT_Wanalyzer_va_list_use_after_va_end
;
400 bool operator== (const va_list_use_after_va_end
&other
) const
402 return (va_list_sm_diagnostic::subclass_equal_p (other
)
403 && 0 == strcmp (m_usage_fnname
, other
.m_usage_fnname
));
406 bool emit (diagnostic_emission_context
&ctxt
) final override
408 return ctxt
.warn ("%qs after %qs", m_usage_fnname
, "va_end");
411 const char *get_kind () const final override
413 return "va_list_use_after_va_end";
416 label_text
describe_state_change (const evdesc::state_change
&change
)
419 if (change
.m_new_state
== m_sm
.m_ended
)
420 m_va_end_event
= change
.m_event_id
;
421 return va_list_sm_diagnostic::describe_state_change (change
);
424 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
428 if (m_va_end_event
.known_p ())
429 return ev
.formatted_print
430 ("%qs on %qE after %qs at %@",
431 m_usage_fnname
, ev
.m_expr
, "va_end", &m_va_end_event
);
433 return ev
.formatted_print
434 ("%qs on %qE after %qs",
435 m_usage_fnname
, ev
.m_expr
, "va_end");
439 if (m_va_end_event
.known_p ())
440 return ev
.formatted_print
441 ("%qs after %qs at %@",
442 m_usage_fnname
, "va_end", &m_va_end_event
);
444 return ev
.formatted_print
446 m_usage_fnname
, "va_end");
451 diagnostic_event_id_t m_va_end_event
;
452 const char *m_usage_fnname
;
455 /* Concrete class for -Wanalyzer-va-list-leak:
456 complain about a va_list in the "started" state that doesn't get after
457 va_end called on it. */
459 class va_list_leak
: public va_list_sm_diagnostic
462 va_list_leak (const va_list_state_machine
&sm
,
463 const svalue
*ap_sval
, tree ap_tree
)
464 : va_list_sm_diagnostic (sm
, ap_sval
, ap_tree
),
465 m_start_event_fnname (NULL
)
469 int get_controlling_option () const final override
471 return OPT_Wanalyzer_va_list_leak
;
474 bool operator== (const va_list_leak
&other
) const
476 return va_list_sm_diagnostic::subclass_equal_p (other
);
479 bool emit (diagnostic_emission_context
&ctxt
) final override
481 return ctxt
.warn ("missing call to %qs", "va_end");
484 const char *get_kind () const final override
{ return "va_list_leak"; }
486 label_text
describe_state_change (const evdesc::state_change
&change
)
489 if (change
.m_new_state
== m_sm
.m_started
)
491 m_start_event
= change
.m_event_id
;
492 m_start_event_fnname
= maybe_get_fnname (change
);
494 return va_list_sm_diagnostic::describe_state_change (change
);
497 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
501 if (m_start_event
.known_p () && m_start_event_fnname
)
502 return ev
.formatted_print
503 ("missing call to %qs on %qE to match %qs at %@",
504 "va_end", ev
.m_expr
, m_start_event_fnname
, &m_start_event
);
506 return ev
.formatted_print
507 ("missing call to %qs on %qE",
508 "va_end", ev
.m_expr
);
512 if (m_start_event
.known_p () && m_start_event_fnname
)
513 return ev
.formatted_print
514 ("missing call to %qs to match %qs at %@",
515 "va_end", m_start_event_fnname
, &m_start_event
);
517 return ev
.formatted_print
518 ("missing call to %qs",
524 diagnostic_event_id_t m_start_event
;
525 const char *m_start_event_fnname
;
528 /* Update state machine for a "va_start" call. */
531 va_list_state_machine::on_va_start (sm_context
&sm_ctxt
,
533 const gcall
*call
) const
535 const svalue
*arg
= get_stateful_arg (sm_ctxt
, call
, 0);
538 /* Transition from start state to "started". */
539 if (sm_ctxt
.get_state (call
, arg
) == m_start
)
540 sm_ctxt
.set_next_state (call
, arg
, m_started
);
544 /* Complain if ARG is in the "ended" state. */
547 va_list_state_machine::check_for_ended_va_list (sm_context
&sm_ctxt
,
548 const supernode
*node
,
551 const char *usage_fnname
) const
553 if (sm_ctxt
.get_state (call
, arg
) == m_ended
)
554 sm_ctxt
.warn (node
, call
, arg
,
555 make_unique
<va_list_use_after_va_end
>
556 (*this, arg
, NULL_TREE
, usage_fnname
));
559 /* Get the svalue with associated va_list_state_machine state for
560 ARG_IDX of CALL to va_copy, if SM_CTXT supports this,
561 or NULL otherwise. */
563 static const svalue
*
564 get_stateful_va_copy_arg (sm_context
&sm_ctxt
,
568 if (const program_state
*new_state
= sm_ctxt
.get_new_program_state ())
570 const region_model
*new_model
= new_state
->m_region_model
;
571 const svalue
*arg
= get_va_copy_arg (new_model
, NULL
, call
, arg_idx
);
577 /* Update state machine for a "va_copy" call. */
580 va_list_state_machine::on_va_copy (sm_context
&sm_ctxt
,
581 const supernode
*node
,
582 const gcall
*call
) const
584 const svalue
*src_arg
= get_stateful_va_copy_arg (sm_ctxt
, call
, 1);
586 check_for_ended_va_list (sm_ctxt
, node
, call
, src_arg
, "va_copy");
588 const svalue
*dst_arg
= get_stateful_arg (sm_ctxt
, call
, 0);
591 /* Transition from start state to "started". */
592 if (sm_ctxt
.get_state (call
, dst_arg
) == m_start
)
593 sm_ctxt
.set_next_state (call
, dst_arg
, m_started
);
597 /* Update state machine for a "va_arg" call. */
600 va_list_state_machine::on_va_arg (sm_context
&sm_ctxt
,
601 const supernode
*node
,
602 const gcall
*call
) const
604 const svalue
*arg
= get_stateful_arg (sm_ctxt
, call
, 0);
606 check_for_ended_va_list (sm_ctxt
, node
, call
, arg
, "va_arg");
609 /* Update state machine for a "va_end" call. */
612 va_list_state_machine::on_va_end (sm_context
&sm_ctxt
,
613 const supernode
*node
,
614 const gcall
*call
) const
616 const svalue
*arg
= get_stateful_arg (sm_ctxt
, call
, 0);
619 state_t s
= sm_ctxt
.get_state (call
, arg
);
620 /* Transition from "started" to "ended". */
622 sm_ctxt
.set_next_state (call
, arg
, m_ended
);
623 else if (s
== m_ended
)
624 check_for_ended_va_list (sm_ctxt
, node
, call
, arg
, "va_end");
628 /* Implementation of state_machine::on_leak vfunc for va_list_state_machine
629 (for complaining about leaks of values in state 'started'). */
631 std::unique_ptr
<pending_diagnostic
>
632 va_list_state_machine::on_leak (tree var
) const
634 return make_unique
<va_list_leak
> (*this, nullptr, var
);
637 } // anonymous namespace
639 /* Internal interface to this file. */
642 make_va_list_state_machine (logger
*logger
)
644 return new va_list_state_machine (logger
);
647 /* Handler for "__builtin_va_start". */
649 class kf_va_start
: public known_function
652 bool matches_call_types_p (const call_details
&) const final override
656 void impl_call_pre (const call_details
&cd
) const final override
;
660 kf_va_start::impl_call_pre (const call_details
&cd
) const
662 region_model
*model
= cd
.get_model ();
663 region_model_manager
*mgr
= cd
.get_manager ();
664 const svalue
*out_ptr
= cd
.get_arg_svalue (0);
665 const region
*out_reg
666 = model
->deref_rvalue (out_ptr
, cd
.get_arg_tree (0), cd
.get_ctxt ());
667 const frame_region
*frame
= model
->get_current_frame ();
669 /* "*out_ptr = &IMPL_REGION;". */
670 const region
*impl_reg
= mgr
->create_region_for_alloca (frame
);
672 /* We abuse the types here, since va_list_type isn't
673 necessarily anything to do with a pointer. */
674 const svalue
*ptr_to_impl_reg
= mgr
->get_ptr_svalue (NULL_TREE
, impl_reg
);
675 model
->set_value (out_reg
, ptr_to_impl_reg
, cd
.get_ctxt ());
677 if (model
->get_stack_depth () > 1)
679 /* The interprocedural case: the frame containing the va_start call
680 will have been populated with any variadic aruguments.
681 Initialize IMPL_REGION with a ptr to var_arg_region 0. */
682 const region
*init_var_arg_reg
= mgr
->get_var_arg_region (frame
, 0);
683 const svalue
*ap_sval
684 = mgr
->get_ptr_svalue (NULL_TREE
, init_var_arg_reg
);
685 model
->set_value (impl_reg
, ap_sval
, cd
.get_ctxt ());
689 /* The frame containing va_start is an entry-point to the analysis,
690 so there won't be any specific var_arg_regions populated within it.
691 Initialize IMPL_REGION as the UNKNOWN_SVALUE to avoid state
692 explosions on repeated calls to va_arg. */
693 const svalue
*unknown_sval
694 = mgr
->get_or_create_unknown_svalue (NULL_TREE
);
695 model
->set_value (impl_reg
, unknown_sval
, cd
.get_ctxt ());
699 /* Handler for "__builtin_va_copy". */
701 class kf_va_copy
: public known_function
704 bool matches_call_types_p (const call_details
&) const final override
708 void impl_call_pre (const call_details
&cd
) const final override
;
712 kf_va_copy::impl_call_pre (const call_details
&cd
) const
714 region_model
*model
= cd
.get_model ();
715 region_model_manager
*mgr
= cd
.get_manager ();
716 const svalue
*out_dst_ptr
= cd
.get_arg_svalue (0);
717 const svalue
*in_va_list
718 = get_va_copy_arg (model
, cd
.get_ctxt (), cd
.get_call_stmt (), 1);
720 = model
->check_for_poison (in_va_list
,
721 get_va_list_diag_arg (cd
.get_arg_tree (1)),
725 const region
*out_dst_reg
726 = model
->deref_rvalue (out_dst_ptr
, cd
.get_arg_tree (0), cd
.get_ctxt ());
728 /* "*out_dst_ptr = &NEW_IMPL_REGION;". */
729 const region
*new_impl_reg
730 = mgr
->create_region_for_alloca (model
->get_current_frame ());
731 const svalue
*ptr_to_new_impl_reg
732 = mgr
->get_ptr_svalue (NULL_TREE
, new_impl_reg
);
733 model
->set_value (out_dst_reg
, ptr_to_new_impl_reg
, cd
.get_ctxt ());
735 if (const region
*old_impl_reg
= in_va_list
->maybe_get_region ())
737 /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);". */
738 const svalue
*existing_sval
739 = model
->get_store_value (old_impl_reg
, cd
.get_ctxt ());
740 model
->set_value (new_impl_reg
, existing_sval
, cd
.get_ctxt ());
744 /* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT. */
747 get_num_variadic_arguments (tree callee_fndecl
,
748 const gcall
*call_stmt
)
750 int num_positional
= 0;
751 for (tree iter_parm
= DECL_ARGUMENTS (callee_fndecl
); iter_parm
;
752 iter_parm
= DECL_CHAIN (iter_parm
))
754 return gimple_call_num_args (call_stmt
) - num_positional
;
757 /* An abstract subclass of pending_diagnostic for diagnostics relating
758 to bad va_arg invocations.
760 This shows the number of variadic arguments at the call of interest.
761 Ideally we'd also be able to highlight individual arguments, but
762 that location information isn't generally available from the middle end. */
764 class va_arg_diagnostic
: public pending_diagnostic
767 /* Override of pending_diagnostic::add_call_event,
768 adding a custom call_event subclass. */
769 void add_call_event (const exploded_edge
&eedge
,
770 checker_path
*emission_path
) override
772 /* As per call_event, but show the number of variadic arguments
774 class va_arg_call_event
: public call_event
777 va_arg_call_event (const exploded_edge
&eedge
,
778 const event_loc_info
&loc_info
,
779 int num_variadic_arguments
)
780 : call_event (eedge
, loc_info
),
781 m_num_variadic_arguments (num_variadic_arguments
)
785 label_text
get_desc (bool can_colorize
) const override
787 return make_label_text_n
788 (can_colorize
, m_num_variadic_arguments
,
789 "calling %qE from %qE with %i variadic argument",
790 "calling %qE from %qE with %i variadic arguments",
791 get_callee_fndecl (),
792 get_caller_fndecl (),
793 m_num_variadic_arguments
);
796 int m_num_variadic_arguments
;
799 const frame_region
*frame_reg
= m_var_arg_reg
->get_frame_region ();
800 const exploded_node
*dst_node
= eedge
.m_dest
;
801 if (dst_node
->get_state ().m_region_model
->get_current_frame ()
804 const exploded_node
*src_node
= eedge
.m_src
;
805 const program_point
&src_point
= src_node
->get_point ();
806 const int src_stack_depth
= src_point
.get_stack_depth ();
807 const gimple
*last_stmt
= src_point
.get_supernode ()->get_last_stmt ();
808 const gcall
*call_stmt
= as_a
<const gcall
*> (last_stmt
);
809 int num_variadic_arguments
810 = get_num_variadic_arguments (dst_node
->get_function ()->decl
,
812 emission_path
->add_event
813 (make_unique
<va_arg_call_event
>
815 event_loc_info (last_stmt
? last_stmt
->location
: UNKNOWN_LOCATION
,
816 src_point
.get_fndecl (),
818 num_variadic_arguments
));
821 pending_diagnostic::add_call_event (eedge
, emission_path
);
825 va_arg_diagnostic (tree va_list_tree
, const var_arg_region
*var_arg_reg
)
826 : m_va_list_tree (va_list_tree
), m_var_arg_reg (var_arg_reg
)
829 bool subclass_equal_p (const pending_diagnostic
&base_other
) const override
831 const va_arg_diagnostic
&other
= (const va_arg_diagnostic
&)base_other
;
832 return (same_tree_p (m_va_list_tree
, other
.m_va_list_tree
)
833 && m_var_arg_reg
== other
.m_var_arg_reg
);
836 /* Get the number of arguments consumed so far from the va_list
837 (*before* this va_arg call). */
838 unsigned get_num_consumed () const
840 return m_var_arg_reg
->get_index ();
843 /* Get a 1-based index of which variadic argument is being consumed. */
844 unsigned get_variadic_index_for_diagnostic () const
846 return get_num_consumed () + 1;
849 /* User-readable expr for the va_list argument to va_arg. */
852 /* The region that the va_arg attempted to access. */
853 const var_arg_region
*m_var_arg_reg
;
856 /* A subclass of pending_diagnostic for complaining about a type mismatch
857 between the result of:
859 and the type of the argument that was passed to the variadic call. */
861 class va_arg_type_mismatch
: public va_arg_diagnostic
864 va_arg_type_mismatch (tree va_list_tree
, const var_arg_region
*var_arg_reg
,
865 tree expected_type
, tree actual_type
)
866 : va_arg_diagnostic (va_list_tree
, var_arg_reg
),
867 m_expected_type (expected_type
), m_actual_type (actual_type
)
870 const char *get_kind () const final override
872 return "va_arg_type_mismatch";
875 bool subclass_equal_p (const pending_diagnostic
&base_other
)
878 if (!va_arg_diagnostic::subclass_equal_p (base_other
))
880 const va_arg_type_mismatch
&other
881 = (const va_arg_type_mismatch
&)base_other
;
882 return (same_tree_p (m_expected_type
, other
.m_expected_type
)
883 && same_tree_p (m_actual_type
, other
.m_actual_type
));
886 int get_controlling_option () const final override
888 return OPT_Wanalyzer_va_arg_type_mismatch
;
891 bool emit (diagnostic_emission_context
&ctxt
) final override
893 /* "CWE-686: Function Call With Incorrect Argument Type". */
896 = ctxt
.warn ("%<va_arg%> expected %qT but received %qT"
897 " for variadic argument %i of %qE",
898 m_expected_type
, m_actual_type
,
899 get_variadic_index_for_diagnostic (), m_va_list_tree
);
903 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
905 return ev
.formatted_print ("%<va_arg%> expected %qT but received %qT"
906 " for variadic argument %i of %qE",
907 m_expected_type
, m_actual_type
,
908 get_variadic_index_for_diagnostic (),
913 tree m_expected_type
;
917 /* A subclass of pending_diagnostic for complaining about a
919 after all of the args in AP have been consumed. */
921 class va_list_exhausted
: public va_arg_diagnostic
924 va_list_exhausted (tree va_list_tree
, const var_arg_region
*var_arg_reg
)
925 : va_arg_diagnostic (va_list_tree
, var_arg_reg
)
928 const char *get_kind () const final override
930 return "va_list_exhausted";
933 int get_controlling_option () const final override
935 return OPT_Wanalyzer_va_list_exhausted
;
938 bool emit (diagnostic_emission_context
&ctxt
) final override
940 /* CWE-685: Function Call With Incorrect Number of Arguments. */
942 bool warned
= ctxt
.warn ("%qE has no more arguments (%i consumed)",
943 m_va_list_tree
, get_num_consumed ());
947 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
949 return ev
.formatted_print ("%qE has no more arguments (%i consumed)",
950 m_va_list_tree
, get_num_consumed ());
955 representable_in_integral_type_p (const svalue
&sval
, const_tree type
)
957 gcc_assert (INTEGRAL_TYPE_P (type
));
959 if (tree cst
= sval
.maybe_get_constant ())
960 return wi::fits_to_tree_p (wi::to_wide (cst
), type
);
965 /* Return true if it's OK to copy ARG_SVAL from ARG_TYPE to LHS_TYPE via
966 va_arg (where argument promotion has already happened). */
969 va_arg_compatible_types_p (tree lhs_type
, tree arg_type
, const svalue
&arg_sval
)
971 if (compat_types_p (arg_type
, lhs_type
))
974 /* It's OK if both types are integer types, where one is signed and the
975 other type the corresponding unsigned type, when the value is
976 representable in both types. */
977 if (INTEGRAL_TYPE_P (lhs_type
)
978 && INTEGRAL_TYPE_P (arg_type
)
979 && TYPE_UNSIGNED (lhs_type
) != TYPE_UNSIGNED (arg_type
)
980 && TYPE_PRECISION (lhs_type
) == TYPE_PRECISION (arg_type
)
981 && representable_in_integral_type_p (arg_sval
, lhs_type
)
982 && representable_in_integral_type_p (arg_sval
, arg_type
))
985 /* It's OK if one type is a pointer to void and the other is a
986 pointer to a character type.
987 This is handled by compat_types_p. */
989 /* Otherwise the types are not compatible. */
993 /* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
994 Otherwise return NULL. */
996 static const var_arg_region
*
997 maybe_get_var_arg_region (const svalue
*ap_sval
)
999 if (const region
*reg
= ap_sval
->maybe_get_region ())
1000 return reg
->dyn_cast_var_arg_region ();
1004 /* Handler for "__builtin_va_arg". */
1006 class kf_va_arg
: public internal_known_function
1009 void impl_call_pre (const call_details
&cd
) const final override
;
1013 kf_va_arg::impl_call_pre (const call_details
&cd
) const
1015 region_model_context
*ctxt
= cd
.get_ctxt ();
1016 region_model
*model
= cd
.get_model ();
1017 region_model_manager
*mgr
= cd
.get_manager ();
1019 const svalue
*in_ptr
= cd
.get_arg_svalue (0);
1020 const region
*ap_reg
1021 = model
->deref_rvalue (in_ptr
, cd
.get_arg_tree (0), ctxt
);
1023 const svalue
*ap_sval
= model
->get_store_value (ap_reg
, ctxt
);
1024 if (const svalue
*cast
= ap_sval
->maybe_undo_cast ())
1027 tree va_list_tree
= get_va_list_diag_arg (cd
.get_arg_tree (0));
1028 ap_sval
= model
->check_for_poison (ap_sval
, va_list_tree
, ap_reg
, ctxt
);
1030 cd
.set_any_lhs_with_defaults ();
1032 if (const region
*impl_reg
= ap_sval
->maybe_get_region ())
1034 const svalue
*old_impl_sval
= model
->get_store_value (impl_reg
, ctxt
);
1035 if (const var_arg_region
*arg_reg
1036 = maybe_get_var_arg_region (old_impl_sval
))
1038 bool saw_problem
= false;
1040 const frame_region
*frame_reg
= arg_reg
->get_frame_region ();
1041 unsigned next_arg_idx
= arg_reg
->get_index ();
1043 if (frame_reg
->get_stack_depth () > 1)
1045 /* The interprocedural case: the called frame will have been
1046 populated with any variadic aruguments.
1047 Attempt to extract arg_reg to cd's return region (which already
1048 has a conjured_svalue), or warn if there's a problem
1049 (incompatible types, or if we've run out of args). */
1050 if (const svalue
*arg_sval
1051 = model
->get_store ()->get_any_binding
1052 (mgr
->get_store_manager (), arg_reg
))
1054 tree lhs_type
= cd
.get_lhs_type ();
1055 tree arg_type
= arg_sval
->get_type ();
1056 if (va_arg_compatible_types_p (lhs_type
, arg_type
, *arg_sval
))
1057 cd
.maybe_set_lhs (arg_sval
);
1061 ctxt
->warn (make_unique
<va_arg_type_mismatch
>
1072 ctxt
->warn (make_unique
<va_list_exhausted
> (va_list_tree
,
1079 /* This frame is an entry-point to the analysis, so there won't be
1080 any specific var_arg_regions populated within it.
1081 We already have a conjured_svalue for the result, so leave
1083 gcc_assert (frame_reg
->get_stack_depth () == 1);
1088 /* Set impl_reg to UNKNOWN to suppress further warnings. */
1089 const svalue
*new_ap_sval
1090 = mgr
->get_or_create_unknown_svalue (impl_reg
->get_type ());
1091 model
->set_value (impl_reg
, new_ap_sval
, ctxt
);
1095 /* Update impl_reg to advance to the next arg. */
1096 const region
*next_var_arg_region
1097 = mgr
->get_var_arg_region (frame_reg
, next_arg_idx
+ 1);
1098 const svalue
*new_ap_sval
1099 = mgr
->get_ptr_svalue (NULL_TREE
, next_var_arg_region
);
1100 model
->set_value (impl_reg
, new_ap_sval
, ctxt
);
1106 /* Handler for "__builtin_va_end". */
1108 class kf_va_end
: public known_function
1111 bool matches_call_types_p (const call_details
&) const
1117 /* Populate KFM with instances of known functions relating to varargs. */
1120 register_varargs_builtins (known_function_manager
&kfm
)
1122 kfm
.add (BUILT_IN_VA_START
, make_unique
<kf_va_start
> ());
1123 kfm
.add (BUILT_IN_VA_COPY
, make_unique
<kf_va_copy
> ());
1124 kfm
.add (IFN_VA_ARG
, make_unique
<kf_va_arg
> ());
1125 kfm
.add (BUILT_IN_VA_END
, make_unique
<kf_va_end
> ());
1130 #endif /* #if ENABLE_ANALYZER */