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