Fix couple of endianness issues in fold_ctor_reference
[official-gcc.git] / gcc / analyzer / sm-file.cc
blob0cfe68217229237b0cd509b40dda911f2607a115
1 /* A state machine for detecting misuses of <stdio.h>'s FILE * API.
2 Copyright (C) 2019-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #define INCLUDE_MEMORY
23 #include "system.h"
24 #include "coretypes.h"
25 #include "make-unique.h"
26 #include "tree.h"
27 #include "function.h"
28 #include "basic-block.h"
29 #include "gimple.h"
30 #include "options.h"
31 #include "diagnostic-path.h"
32 #include "diagnostic-metadata.h"
33 #include "analyzer/analyzer.h"
34 #include "diagnostic-event-id.h"
35 #include "analyzer/analyzer-logging.h"
36 #include "analyzer/sm.h"
37 #include "analyzer/pending-diagnostic.h"
38 #include "analyzer/function-set.h"
39 #include "analyzer/analyzer-selftests.h"
40 #include "selftest.h"
41 #include "analyzer/call-string.h"
42 #include "analyzer/program-point.h"
43 #include "analyzer/store.h"
44 #include "analyzer/region-model.h"
45 #include "analyzer/call-details.h"
47 #if ENABLE_ANALYZER
49 namespace ana {
51 namespace {
53 /* A state machine for detecting misuses of <stdio.h>'s FILE * API. */
55 class fileptr_state_machine : public state_machine
57 public:
58 fileptr_state_machine (logger *logger);
60 bool inherited_state_p () const final override { return false; }
62 state_machine::state_t
63 get_default_state (const svalue *sval) const final override
65 if (tree cst = sval->maybe_get_constant ())
67 if (zerop (cst))
68 return m_null;
70 return m_start;
73 bool on_stmt (sm_context *sm_ctxt,
74 const supernode *node,
75 const gimple *stmt) const final override;
77 void on_condition (sm_context *sm_ctxt,
78 const supernode *node,
79 const gimple *stmt,
80 const svalue *lhs,
81 enum tree_code op,
82 const svalue *rhs) const final override;
84 bool can_purge_p (state_t s) const final override;
85 std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
87 /* State for a FILE * returned from fopen that hasn't been checked for
88 NULL.
89 It could be an open stream, or could be NULL. */
90 state_t m_unchecked;
92 /* State for a FILE * that's known to be NULL. */
93 state_t m_null;
95 /* State for a FILE * that's known to be a non-NULL open stream. */
96 state_t m_nonnull;
98 /* State for a FILE * that's had fclose called on it. */
99 state_t m_closed;
101 /* Stop state, for a FILE * we don't want to track any more. */
102 state_t m_stop;
105 /* Base class for diagnostics relative to fileptr_state_machine. */
107 class file_diagnostic : public pending_diagnostic
109 public:
110 file_diagnostic (const fileptr_state_machine &sm, tree arg)
111 : m_sm (sm), m_arg (arg)
114 bool subclass_equal_p (const pending_diagnostic &base_other) const override
116 return same_tree_p (m_arg, ((const file_diagnostic &)base_other).m_arg);
119 label_text describe_state_change (const evdesc::state_change &change)
120 override
122 if (change.m_old_state == m_sm.get_start_state ()
123 && change.m_new_state == m_sm.m_unchecked)
124 // TODO: verify that it's the fopen stmt, not a copy
125 return label_text::borrow ("opened here");
126 if (change.m_old_state == m_sm.m_unchecked
127 && change.m_new_state == m_sm.m_nonnull)
129 if (change.m_expr)
130 return change.formatted_print ("assuming %qE is non-NULL",
131 change.m_expr);
132 else
133 return change.formatted_print ("assuming FILE * is non-NULL");
135 if (change.m_new_state == m_sm.m_null)
137 if (change.m_expr)
138 return change.formatted_print ("assuming %qE is NULL",
139 change.m_expr);
140 else
141 return change.formatted_print ("assuming FILE * is NULL");
143 return label_text ();
146 diagnostic_event::meaning
147 get_meaning_for_state_change (const evdesc::state_change &change)
148 const final override
150 if (change.m_old_state == m_sm.get_start_state ()
151 && change.m_new_state == m_sm.m_unchecked)
152 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
153 diagnostic_event::NOUN_resource);
154 if (change.m_new_state == m_sm.m_closed)
155 return diagnostic_event::meaning (diagnostic_event::VERB_release,
156 diagnostic_event::NOUN_resource);
157 return diagnostic_event::meaning ();
160 protected:
161 const fileptr_state_machine &m_sm;
162 tree m_arg;
165 class double_fclose : public file_diagnostic
167 public:
168 double_fclose (const fileptr_state_machine &sm, tree arg)
169 : file_diagnostic (sm, arg)
172 const char *get_kind () const final override { return "double_fclose"; }
174 int get_controlling_option () const final override
176 return OPT_Wanalyzer_double_fclose;
179 bool emit (rich_location *rich_loc, logger *) final override
181 diagnostic_metadata m;
182 /* CWE-1341: Multiple Releases of Same Resource or Handle. */
183 m.add_cwe (1341);
184 return warning_meta (rich_loc, m, get_controlling_option (),
185 "double %<fclose%> of FILE %qE",
186 m_arg);
189 label_text describe_state_change (const evdesc::state_change &change)
190 override
192 if (change.m_new_state == m_sm.m_closed)
194 m_first_fclose_event = change.m_event_id;
195 return change.formatted_print ("first %qs here", "fclose");
197 return file_diagnostic::describe_state_change (change);
200 label_text describe_final_event (const evdesc::final_event &ev) final override
202 if (m_first_fclose_event.known_p ())
203 return ev.formatted_print ("second %qs here; first %qs was at %@",
204 "fclose", "fclose",
205 &m_first_fclose_event);
206 return ev.formatted_print ("second %qs here", "fclose");
209 private:
210 diagnostic_event_id_t m_first_fclose_event;
213 class file_leak : public file_diagnostic
215 public:
216 file_leak (const fileptr_state_machine &sm, tree arg)
217 : file_diagnostic (sm, arg)
220 const char *get_kind () const final override { return "file_leak"; }
222 int get_controlling_option () const final override
224 return OPT_Wanalyzer_file_leak;
227 bool emit (rich_location *rich_loc, logger *) final override
229 diagnostic_metadata m;
230 /* CWE-775: "Missing Release of File Descriptor or Handle after
231 Effective Lifetime". */
232 m.add_cwe (775);
233 if (m_arg)
234 return warning_meta (rich_loc, m, get_controlling_option (),
235 "leak of FILE %qE",
236 m_arg);
237 else
238 return warning_meta (rich_loc, m, get_controlling_option (),
239 "leak of FILE");
242 label_text describe_state_change (const evdesc::state_change &change)
243 final override
245 if (change.m_new_state == m_sm.m_unchecked)
247 m_fopen_event = change.m_event_id;
248 return label_text::borrow ("opened here");
250 return file_diagnostic::describe_state_change (change);
253 label_text describe_final_event (const evdesc::final_event &ev) final override
255 if (m_fopen_event.known_p ())
257 if (ev.m_expr)
258 return ev.formatted_print ("%qE leaks here; was opened at %@",
259 ev.m_expr, &m_fopen_event);
260 else
261 return ev.formatted_print ("leaks here; was opened at %@",
262 &m_fopen_event);
264 else
266 if (ev.m_expr)
267 return ev.formatted_print ("%qE leaks here", ev.m_expr);
268 else
269 return ev.formatted_print ("leaks here");
273 private:
274 diagnostic_event_id_t m_fopen_event;
277 /* fileptr_state_machine's ctor. */
279 fileptr_state_machine::fileptr_state_machine (logger *logger)
280 : state_machine ("file", logger)
282 m_unchecked = add_state ("unchecked");
283 m_null = add_state ("null");
284 m_nonnull = add_state ("nonnull");
285 m_closed = add_state ("closed");
286 m_stop = add_state ("stop");
289 /* Get a set of functions that are known to take a FILE * that must be open,
290 and are known to not close it. */
292 static function_set
293 get_file_using_fns ()
295 // TODO: populate this list more fully
296 static const char * const funcnames[] = {
297 /* This array must be kept sorted. */
298 "__fbufsize",
299 "__flbf",
300 "__fpending",
301 "__fpurge",
302 "__freadable",
303 "__freading",
304 "__fsetlocking",
305 "__fwritable",
306 "__fwriting",
307 "clearerr",
308 "clearerr_unlocked",
309 "feof",
310 "feof_unlocked",
311 "ferror",
312 "ferror_unlocked",
313 "fflush", // safe to call with NULL
314 "fflush_unlocked", // safe to call with NULL
315 "fgetc",
316 "fgetc_unlocked",
317 "fgetpos",
318 "fgets",
319 "fgets_unlocked",
320 "fgetwc_unlocked",
321 "fgetws_unlocked",
322 "fileno",
323 "fileno_unlocked",
324 "fprintf",
325 "fputc",
326 "fputc_unlocked",
327 "fputs",
328 "fputs_unlocked",
329 "fputwc_unlocked",
330 "fputws_unlocked",
331 "fread_unlocked",
332 "fseek",
333 "fsetpos",
334 "ftell",
335 "fwrite_unlocked",
336 "getc",
337 "getc_unlocked",
338 "getwc_unlocked",
339 "putc",
340 "putc_unlocked",
341 "rewind",
342 "setbuf",
343 "setbuffer",
344 "setlinebuf",
345 "setvbuf",
346 "ungetc",
347 "vfprintf"
349 const size_t count = ARRAY_SIZE (funcnames);
350 function_set fs (funcnames, count);
351 return fs;
354 /* Return true if FNDECL is known to require an open FILE *, and is known
355 to not close it. */
357 static bool
358 is_file_using_fn_p (tree fndecl)
360 function_set fs = get_file_using_fns ();
361 if (fs.contains_decl_p (fndecl))
362 return true;
364 /* Also support variants of these names prefixed with "_IO_". */
365 const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
366 if (startswith (name, "_IO_") && fs.contains_name_p (name + 4))
367 return true;
369 return false;
372 /* Implementation of state_machine::on_stmt vfunc for fileptr_state_machine. */
374 bool
375 fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
376 const supernode *node,
377 const gimple *stmt) const
379 if (const gcall *call = dyn_cast <const gcall *> (stmt))
380 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
382 if (is_named_call_p (callee_fndecl, "fopen", call, 2))
384 tree lhs = gimple_call_lhs (call);
385 if (lhs)
386 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
387 else
389 /* TODO: report leak. */
391 return true;
394 if (is_named_call_p (callee_fndecl, "fclose", call, 1))
396 tree arg = gimple_call_arg (call, 0);
398 sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
400 // TODO: is it safe to call fclose (NULL) ?
401 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_closed);
402 sm_ctxt->on_transition (node, stmt, arg, m_null, m_closed);
404 sm_ctxt->on_transition (node, stmt , arg, m_nonnull, m_closed);
406 if (sm_ctxt->get_state (stmt, arg) == m_closed)
408 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
409 sm_ctxt->warn (node, stmt, arg,
410 make_unique<double_fclose> (*this, diag_arg));
411 sm_ctxt->set_next_state (stmt, arg, m_stop);
413 return true;
416 if (is_file_using_fn_p (callee_fndecl))
418 // TODO: operations on unchecked file
419 return true;
421 // etc
424 return false;
427 /* Implementation of state_machine::on_condition vfunc for
428 fileptr_state_machine.
429 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
431 void
432 fileptr_state_machine::on_condition (sm_context *sm_ctxt,
433 const supernode *node,
434 const gimple *stmt,
435 const svalue *lhs,
436 enum tree_code op,
437 const svalue *rhs) const
439 if (!rhs->all_zeroes_p ())
440 return;
442 // TODO: has to be a FILE *, specifically
443 if (!any_pointer_p (lhs))
444 return;
445 // TODO: has to be a FILE *, specifically
446 if (!any_pointer_p (rhs))
447 return;
449 if (op == NE_EXPR)
451 log ("got 'ARG != 0' match");
452 sm_ctxt->on_transition (node, stmt,
453 lhs, m_unchecked, m_nonnull);
455 else if (op == EQ_EXPR)
457 log ("got 'ARG == 0' match");
458 sm_ctxt->on_transition (node, stmt,
459 lhs, m_unchecked, m_null);
463 /* Implementation of state_machine::can_purge_p vfunc for fileptr_state_machine.
464 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
465 (to avoid false leak reports). */
467 bool
468 fileptr_state_machine::can_purge_p (state_t s) const
470 return s != m_unchecked && s != m_nonnull;
473 /* Implementation of state_machine::on_leak vfunc for
474 fileptr_state_machine, for complaining about leaks of FILE * in
475 state 'unchecked' and 'nonnull'. */
477 std::unique_ptr<pending_diagnostic>
478 fileptr_state_machine::on_leak (tree var) const
480 return make_unique<file_leak> (*this, var);
483 } // anonymous namespace
485 /* Internal interface to this file. */
487 state_machine *
488 make_fileptr_state_machine (logger *logger)
490 return new fileptr_state_machine (logger);
493 /* Handler for various stdio-related builtins that merely have external
494 effects that are out of scope for the analyzer: we only want to model
495 the effects on the return value. */
497 class kf_stdio_output_fn : public known_function
499 public:
500 bool matches_call_types_p (const call_details &) const final override
502 return true;
505 /* A no-op; we just want the conjured return value. */
508 /* Handler for "ferror"". */
510 class kf_ferror : public known_function
512 public:
513 bool matches_call_types_p (const call_details &cd) const final override
515 return (cd.num_args () == 1
516 && cd.arg_is_pointer_p (0));
519 /* No side effects. */
522 /* Handler for "fileno"". */
524 class kf_fileno : public known_function
526 public:
527 bool matches_call_types_p (const call_details &cd) const final override
529 return (cd.num_args () == 1
530 && cd.arg_is_pointer_p (0));
533 /* No side effects. */
536 /* Handler for "fgets" and "fgets_unlocked". */
538 class kf_fgets : public known_function
540 public:
541 bool matches_call_types_p (const call_details &cd) const final override
543 return (cd.num_args () == 3
544 && cd.arg_is_pointer_p (0)
545 && cd.arg_is_pointer_p (2));
548 void impl_call_pre (const call_details &cd) const final override
550 /* Ideally we would bifurcate state here between the
551 error vs no error cases. */
552 region_model *model = cd.get_model ();
553 const svalue *ptr_sval = cd.get_arg_svalue (0);
554 if (const region *reg = ptr_sval->maybe_get_region ())
556 const region *base_reg = reg->get_base_region ();
557 const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
558 model->set_value (base_reg, new_sval, cd.get_ctxt ());
563 /* Handler for "fread".
564 size_t fread(void *restrict buffer, size_t size, size_t count,
565 FILE *restrict stream);
566 See e.g. https://en.cppreference.com/w/c/io/fread
567 and https://www.man7.org/linux/man-pages/man3/fread.3.html */
569 class kf_fread : public known_function
571 public:
572 bool matches_call_types_p (const call_details &cd) const final override
574 return (cd.num_args () == 4
575 && cd.arg_is_pointer_p (0)
576 && cd.arg_is_size_p (1)
577 && cd.arg_is_size_p (2)
578 && cd.arg_is_pointer_p (3));
581 /* For now, assume that any call to "fread" fully clobbers the buffer
582 passed in. This isn't quite correct (e.g. errors, partial reads;
583 see PR analyzer/108689), but at least stops us falsely complaining
584 about the buffer being uninitialized. */
585 void impl_call_pre (const call_details &cd) const final override
587 region_model *model = cd.get_model ();
588 const svalue *ptr_sval = cd.get_arg_svalue (0);
589 if (const region *reg = ptr_sval->maybe_get_region ())
591 const region *base_reg = reg->get_base_region ();
592 const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
593 model->set_value (base_reg, new_sval, cd.get_ctxt ());
598 /* Handler for "getc"". */
600 class kf_getc : public known_function
602 public:
603 bool matches_call_types_p (const call_details &cd) const final override
605 return (cd.num_args () == 1
606 && cd.arg_is_pointer_p (0));
609 /* No side effects. */
612 /* Handler for "getchar"". */
614 class kf_getchar : public known_function
616 public:
617 bool matches_call_types_p (const call_details &cd) const final override
619 return cd.num_args () == 0;
622 /* Empty. No side-effects (tracking stream state is out-of-scope
623 for the analyzer). */
626 /* Populate KFM with instances of known functions relating to
627 stdio streams. */
629 void
630 register_known_file_functions (known_function_manager &kfm)
632 kfm.add (BUILT_IN_FPRINTF, make_unique<kf_stdio_output_fn> ());
633 kfm.add (BUILT_IN_FPRINTF_UNLOCKED, make_unique<kf_stdio_output_fn> ());
634 kfm.add (BUILT_IN_FPUTC, make_unique<kf_stdio_output_fn> ());
635 kfm.add (BUILT_IN_FPUTC_UNLOCKED, make_unique<kf_stdio_output_fn> ());
636 kfm.add (BUILT_IN_FPUTS, make_unique<kf_stdio_output_fn> ());
637 kfm.add (BUILT_IN_FPUTS_UNLOCKED, make_unique<kf_stdio_output_fn> ());
638 kfm.add (BUILT_IN_FWRITE, make_unique<kf_stdio_output_fn> ());
639 kfm.add (BUILT_IN_FWRITE_UNLOCKED, make_unique<kf_stdio_output_fn> ());
640 kfm.add (BUILT_IN_PRINTF, make_unique<kf_stdio_output_fn> ());
641 kfm.add (BUILT_IN_PRINTF_UNLOCKED, make_unique<kf_stdio_output_fn> ());
642 kfm.add (BUILT_IN_PUTC, make_unique<kf_stdio_output_fn> ());
643 kfm.add (BUILT_IN_PUTCHAR, make_unique<kf_stdio_output_fn> ());
644 kfm.add (BUILT_IN_PUTCHAR_UNLOCKED, make_unique<kf_stdio_output_fn> ());
645 kfm.add (BUILT_IN_PUTC_UNLOCKED, make_unique<kf_stdio_output_fn> ());
646 kfm.add (BUILT_IN_PUTS, make_unique<kf_stdio_output_fn> ());
647 kfm.add (BUILT_IN_PUTS_UNLOCKED, make_unique<kf_stdio_output_fn> ());
648 kfm.add (BUILT_IN_VFPRINTF, make_unique<kf_stdio_output_fn> ());
649 kfm.add (BUILT_IN_VPRINTF, make_unique<kf_stdio_output_fn> ());
651 kfm.add ("ferror", make_unique<kf_ferror> ());
652 kfm.add ("fgets", make_unique<kf_fgets> ());
653 kfm.add ("fgets_unlocked", make_unique<kf_fgets> ()); // non-standard
654 kfm.add ("fileno", make_unique<kf_fileno> ());
655 kfm.add ("fread", make_unique<kf_fread> ());
656 kfm.add ("getc", make_unique<kf_getc> ());
657 kfm.add ("getchar", make_unique<kf_getchar> ());
660 #if CHECKING_P
662 namespace selftest {
664 /* Run all of the selftests within this file. */
666 void
667 analyzer_sm_file_cc_tests ()
669 function_set fs = get_file_using_fns ();
670 fs.assert_sorted ();
671 fs.assert_sane ();
674 } // namespace selftest
676 #endif /* CHECKING_P */
678 } // namespace ana
680 #endif /* #if ENABLE_ANALYZER */