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