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