c++: Tweaks for -Wredundant-move [PR107363]
[official-gcc.git] / gcc / analyzer / sm-fd.cc
blobda0e92b511379da38f59d0446c7c49974e7f45dd
1 /* A state machine for detecting misuses of POSIX file descriptor APIs.
2 Copyright (C) 2019-2022 Free Software Foundation, Inc.
3 Contributed by Immad Mir <mir@sourceware.org>.
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 "stringpool.h"
41 #include "attribs.h"
42 #include "analyzer/call-string.h"
43 #include "analyzer/program-point.h"
44 #include "analyzer/store.h"
45 #include "analyzer/region-model.h"
46 #include "bitmap.h"
47 #include "analyzer/program-state.h"
49 #if ENABLE_ANALYZER
51 namespace ana {
53 namespace {
55 /* An enum for distinguishing between three different access modes. */
57 enum access_mode
59 READ_WRITE,
60 READ_ONLY,
61 WRITE_ONLY
64 enum access_directions
66 DIRS_READ_WRITE,
67 DIRS_READ,
68 DIRS_WRITE
71 /* An enum for distinguishing between dup, dup2 and dup3. */
72 enum dup
74 DUP_1,
75 DUP_2,
76 DUP_3
79 class fd_state_machine : public state_machine
81 public:
82 fd_state_machine (logger *logger);
84 bool
85 inherited_state_p () const final override
87 return false;
90 state_machine::state_t
91 get_default_state (const svalue *sval) const final override
93 if (tree cst = sval->maybe_get_constant ())
95 if (TREE_CODE (cst) == INTEGER_CST)
97 int val = TREE_INT_CST_LOW (cst);
98 if (val >= 0)
99 return m_constant_fd;
100 else
101 return m_invalid;
104 return m_start;
107 bool on_stmt (sm_context *sm_ctxt, const supernode *node,
108 const gimple *stmt) const final override;
110 void on_condition (sm_context *sm_ctxt, const supernode *node,
111 const gimple *stmt, const svalue *lhs, const tree_code op,
112 const svalue *rhs) const final override;
114 bool can_purge_p (state_t s) const final override;
115 std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
117 bool is_unchecked_fd_p (state_t s) const;
118 bool is_valid_fd_p (state_t s) const;
119 bool is_closed_fd_p (state_t s) const;
120 bool is_constant_fd_p (state_t s) const;
121 bool is_readonly_fd_p (state_t s) const;
122 bool is_writeonly_fd_p (state_t s) const;
123 enum access_mode get_access_mode_from_flag (int flag) const;
124 /* Function for one-to-one correspondence between valid
125 and unchecked states. */
126 state_t valid_to_unchecked_state (state_t state) const;
128 void mark_as_valid_fd (region_model *model,
129 sm_state_map *smap,
130 const svalue *fd_sval,
131 const extrinsic_state &ext_state) const;
133 /* State for a constant file descriptor (>= 0) */
134 state_t m_constant_fd;
136 /* States representing a file descriptor that hasn't yet been
137 checked for validity after opening, for three different
138 access modes. */
139 state_t m_unchecked_read_write;
141 state_t m_unchecked_read_only;
143 state_t m_unchecked_write_only;
145 /* States for representing a file descriptor that is known to be valid (>=
146 0), for three different access modes. */
147 state_t m_valid_read_write;
149 state_t m_valid_read_only;
151 state_t m_valid_write_only;
153 /* State for a file descriptor that is known to be invalid (< 0). */
154 state_t m_invalid;
156 /* State for a file descriptor that has been closed. */
157 state_t m_closed;
159 /* State for a file descriptor that we do not want to track anymore . */
160 state_t m_stop;
162 private:
163 void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
164 const gcall *call) const;
165 void on_creat (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
166 const gcall *call) const;
167 void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
168 const gcall *call) const;
169 void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
170 const gcall *call, const tree callee_fndecl) const;
171 void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
172 const gcall *call, const tree callee_fndecl) const;
173 void check_for_open_fd (sm_context *sm_ctxt, const supernode *node,
174 const gimple *stmt, const gcall *call,
175 const tree callee_fndecl,
176 enum access_directions access_fn) const;
178 void make_valid_transitions_on_condition (sm_context *sm_ctxt,
179 const supernode *node,
180 const gimple *stmt,
181 const svalue *lhs) const;
182 void make_invalid_transitions_on_condition (sm_context *sm_ctxt,
183 const supernode *node,
184 const gimple *stmt,
185 const svalue *lhs) const;
186 void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node,
187 const gimple *stmt, const gcall *call,
188 const tree callee_fndecl, const char *attr_name,
189 access_directions fd_attr_access_dir) const;
190 void check_for_dup (sm_context *sm_ctxt, const supernode *node,
191 const gimple *stmt, const gcall *call, const tree callee_fndecl,
192 enum dup kind) const;
195 /* Base diagnostic class relative to fd_state_machine. */
196 class fd_diagnostic : public pending_diagnostic
198 public:
199 fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
203 bool
204 subclass_equal_p (const pending_diagnostic &base_other) const override
206 return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg);
209 label_text
210 describe_state_change (const evdesc::state_change &change) override
212 if (change.m_old_state == m_sm.get_start_state ()
213 && (m_sm.is_unchecked_fd_p (change.m_new_state)
214 || m_sm.is_valid_fd_p (change.m_new_state)))
216 if (change.m_new_state == m_sm.m_unchecked_read_write
217 || change.m_new_state == m_sm.m_valid_read_write)
218 return change.formatted_print ("opened here as read-write");
220 if (change.m_new_state == m_sm.m_unchecked_read_only
221 || change.m_new_state == m_sm.m_valid_read_only)
222 return change.formatted_print ("opened here as read-only");
224 if (change.m_new_state == m_sm.m_unchecked_write_only
225 || change.m_new_state == m_sm.m_valid_write_only)
226 return change.formatted_print ("opened here as write-only");
229 if (change.m_new_state == m_sm.m_closed)
230 return change.formatted_print ("closed here");
232 if (m_sm.is_unchecked_fd_p (change.m_old_state)
233 && m_sm.is_valid_fd_p (change.m_new_state))
235 if (change.m_expr)
236 return change.formatted_print (
237 "assuming %qE is a valid file descriptor (>= 0)", change.m_expr);
238 else
239 return change.formatted_print ("assuming a valid file descriptor");
242 if (m_sm.is_unchecked_fd_p (change.m_old_state)
243 && change.m_new_state == m_sm.m_invalid)
245 if (change.m_expr)
246 return change.formatted_print (
247 "assuming %qE is an invalid file descriptor (< 0)",
248 change.m_expr);
249 else
250 return change.formatted_print ("assuming an invalid file descriptor");
253 return label_text ();
256 diagnostic_event::meaning
257 get_meaning_for_state_change (
258 const evdesc::state_change &change) const final override
260 if (change.m_old_state == m_sm.get_start_state ()
261 && (m_sm.is_unchecked_fd_p (change.m_new_state)))
262 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
263 diagnostic_event::NOUN_resource);
264 if (change.m_new_state == m_sm.m_closed)
265 return diagnostic_event::meaning (diagnostic_event::VERB_release,
266 diagnostic_event::NOUN_resource);
267 return diagnostic_event::meaning ();
270 protected:
271 const fd_state_machine &m_sm;
272 tree m_arg;
275 class fd_param_diagnostic : public fd_diagnostic
277 public:
278 fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
279 const char *attr_name, int arg_idx)
280 : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
281 m_attr_name (attr_name), m_arg_idx (arg_idx)
285 fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
286 : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
287 m_attr_name (NULL), m_arg_idx (-1)
291 bool
292 subclass_equal_p (const pending_diagnostic &base_other) const override
294 const fd_param_diagnostic &sub_other
295 = (const fd_param_diagnostic &)base_other;
296 return (same_tree_p (m_arg, sub_other.m_arg)
297 && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)
298 && m_arg_idx == sub_other.m_arg_idx
299 && ((m_attr_name)
300 ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)
301 : true));
304 void
305 inform_filedescriptor_attribute (access_directions fd_dir)
308 if (m_attr_name)
309 switch (fd_dir)
311 case DIRS_READ_WRITE:
312 inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
313 "argument %d of %qD must be an open file descriptor, due to "
314 "%<__attribute__((%s(%d)))%>",
315 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
316 break;
317 case DIRS_WRITE:
318 inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
319 "argument %d of %qD must be a readable file descriptor, due "
320 "to %<__attribute__((%s(%d)))%>",
321 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
322 break;
323 case DIRS_READ:
324 inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
325 "argument %d of %qD must be a writable file descriptor, due "
326 "to %<__attribute__((%s(%d)))%>",
327 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
328 break;
332 protected:
333 tree m_callee_fndecl;
334 const char *m_attr_name;
335 /* ARG_IDX is 0-based. */
336 int m_arg_idx;
339 class fd_leak : public fd_diagnostic
341 public:
342 fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {}
344 const char *
345 get_kind () const final override
347 return "fd_leak";
351 get_controlling_option () const final override
353 return OPT_Wanalyzer_fd_leak;
356 bool
357 emit (rich_location *rich_loc) final override
359 /*CWE-775: Missing Release of File Descriptor or Handle after Effective
360 Lifetime
362 diagnostic_metadata m;
363 m.add_cwe (775);
364 if (m_arg)
365 return warning_meta (rich_loc, m, get_controlling_option (),
366 "leak of file descriptor %qE", m_arg);
367 else
368 return warning_meta (rich_loc, m, get_controlling_option (),
369 "leak of file descriptor");
372 label_text
373 describe_state_change (const evdesc::state_change &change) final override
375 if (m_sm.is_unchecked_fd_p (change.m_new_state))
377 m_open_event = change.m_event_id;
378 return label_text::borrow ("opened here");
381 return fd_diagnostic::describe_state_change (change);
384 label_text
385 describe_final_event (const evdesc::final_event &ev) final override
387 if (m_open_event.known_p ())
389 if (ev.m_expr)
390 return ev.formatted_print ("%qE leaks here; was opened at %@",
391 ev.m_expr, &m_open_event);
392 else
393 return ev.formatted_print ("leaks here; was opened at %@",
394 &m_open_event);
396 else
398 if (ev.m_expr)
399 return ev.formatted_print ("%qE leaks here", ev.m_expr);
400 else
401 return ev.formatted_print ("leaks here");
405 private:
406 diagnostic_event_id_t m_open_event;
409 class fd_access_mode_mismatch : public fd_param_diagnostic
411 public:
412 fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
413 enum access_directions fd_dir,
414 const tree callee_fndecl, const char *attr_name,
415 int arg_idx)
416 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
417 m_fd_dir (fd_dir)
422 fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
423 enum access_directions fd_dir,
424 const tree callee_fndecl)
425 : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
429 const char *
430 get_kind () const final override
432 return "fd_access_mode_mismatch";
436 get_controlling_option () const final override
438 return OPT_Wanalyzer_fd_access_mode_mismatch;
441 bool
442 emit (rich_location *rich_loc) final override
444 bool warned;
445 switch (m_fd_dir)
447 case DIRS_READ:
448 warned = warning_at (rich_loc, get_controlling_option (),
449 "%qE on read-only file descriptor %qE",
450 m_callee_fndecl, m_arg);
451 break;
452 case DIRS_WRITE:
453 warned = warning_at (rich_loc, get_controlling_option (),
454 "%qE on write-only file descriptor %qE",
455 m_callee_fndecl, m_arg);
456 break;
457 default:
458 gcc_unreachable ();
460 if (warned)
461 inform_filedescriptor_attribute (m_fd_dir);
462 return warned;
465 label_text
466 describe_final_event (const evdesc::final_event &ev) final override
468 switch (m_fd_dir)
470 case DIRS_READ:
471 return ev.formatted_print ("%qE on read-only file descriptor %qE",
472 m_callee_fndecl, m_arg);
473 case DIRS_WRITE:
474 return ev.formatted_print ("%qE on write-only file descriptor %qE",
475 m_callee_fndecl, m_arg);
476 default:
477 gcc_unreachable ();
481 private:
482 enum access_directions m_fd_dir;
485 class fd_double_close : public fd_diagnostic
487 public:
488 fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
492 const char *
493 get_kind () const final override
495 return "fd_double_close";
499 get_controlling_option () const final override
501 return OPT_Wanalyzer_fd_double_close;
503 bool
504 emit (rich_location *rich_loc) final override
506 diagnostic_metadata m;
507 // CWE-1341: Multiple Releases of Same Resource or Handle
508 m.add_cwe (1341);
509 return warning_meta (rich_loc, m, get_controlling_option (),
510 "double %<close%> of file descriptor %qE", m_arg);
513 label_text
514 describe_state_change (const evdesc::state_change &change) override
516 if (m_sm.is_unchecked_fd_p (change.m_new_state))
517 return label_text::borrow ("opened here");
519 if (change.m_new_state == m_sm.m_closed)
521 m_first_close_event = change.m_event_id;
522 return change.formatted_print ("first %qs here", "close");
524 return fd_diagnostic::describe_state_change (change);
527 label_text
528 describe_final_event (const evdesc::final_event &ev) final override
530 if (m_first_close_event.known_p ())
531 return ev.formatted_print ("second %qs here; first %qs was at %@",
532 "close", "close", &m_first_close_event);
533 return ev.formatted_print ("second %qs here", "close");
536 private:
537 diagnostic_event_id_t m_first_close_event;
540 class fd_use_after_close : public fd_param_diagnostic
542 public:
543 fd_use_after_close (const fd_state_machine &sm, tree arg,
544 const tree callee_fndecl, const char *attr_name,
545 int arg_idx)
546 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
550 fd_use_after_close (const fd_state_machine &sm, tree arg,
551 const tree callee_fndecl)
552 : fd_param_diagnostic (sm, arg, callee_fndecl)
556 const char *
557 get_kind () const final override
559 return "fd_use_after_close";
563 get_controlling_option () const final override
565 return OPT_Wanalyzer_fd_use_after_close;
568 bool
569 emit (rich_location *rich_loc) final override
571 bool warned;
572 warned = warning_at (rich_loc, get_controlling_option (),
573 "%qE on closed file descriptor %qE", m_callee_fndecl,
574 m_arg);
575 if (warned)
576 inform_filedescriptor_attribute (DIRS_READ_WRITE);
577 return warned;
580 label_text
581 describe_state_change (const evdesc::state_change &change) override
583 if (m_sm.is_unchecked_fd_p (change.m_new_state))
584 return label_text::borrow ("opened here");
586 if (change.m_new_state == m_sm.m_closed)
588 m_first_close_event = change.m_event_id;
589 return change.formatted_print ("closed here");
592 return fd_diagnostic::describe_state_change (change);
595 label_text
596 describe_final_event (const evdesc::final_event &ev) final override
598 if (m_first_close_event.known_p ())
599 return ev.formatted_print (
600 "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,
601 m_arg, "close", &m_first_close_event);
602 else
603 return ev.formatted_print ("%qE on closed file descriptor %qE",
604 m_callee_fndecl, m_arg);
607 private:
608 diagnostic_event_id_t m_first_close_event;
611 class fd_use_without_check : public fd_param_diagnostic
613 public:
614 fd_use_without_check (const fd_state_machine &sm, tree arg,
615 const tree callee_fndecl, const char *attr_name,
616 int arg_idx)
617 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
621 fd_use_without_check (const fd_state_machine &sm, tree arg,
622 const tree callee_fndecl)
623 : fd_param_diagnostic (sm, arg, callee_fndecl)
627 const char *
628 get_kind () const final override
630 return "fd_use_without_check";
634 get_controlling_option () const final override
636 return OPT_Wanalyzer_fd_use_without_check;
639 bool
640 emit (rich_location *rich_loc) final override
642 bool warned;
643 warned = warning_at (rich_loc, get_controlling_option (),
644 "%qE on possibly invalid file descriptor %qE",
645 m_callee_fndecl, m_arg);
646 if (warned)
647 inform_filedescriptor_attribute (DIRS_READ_WRITE);
648 return warned;
651 label_text
652 describe_state_change (const evdesc::state_change &change) override
654 if (m_sm.is_unchecked_fd_p (change.m_new_state))
656 m_first_open_event = change.m_event_id;
657 return label_text::borrow ("opened here");
660 return fd_diagnostic::describe_state_change (change);
663 label_text
664 describe_final_event (const evdesc::final_event &ev) final override
666 if (m_first_open_event.known_p ())
667 return ev.formatted_print (
668 "%qE could be invalid: unchecked value from %@", m_arg,
669 &m_first_open_event);
670 else
671 return ev.formatted_print ("%qE could be invalid", m_arg);
674 private:
675 diagnostic_event_id_t m_first_open_event;
678 fd_state_machine::fd_state_machine (logger *logger)
679 : state_machine ("file-descriptor", logger),
680 m_constant_fd (add_state ("fd-constant")),
681 m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
682 m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
683 m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
684 m_valid_read_write (add_state ("fd-valid-read-write")),
685 m_valid_read_only (add_state ("fd-valid-read-only")),
686 m_valid_write_only (add_state ("fd-valid-write-only")),
687 m_invalid (add_state ("fd-invalid")),
688 m_closed (add_state ("fd-closed")),
689 m_stop (add_state ("fd-stop"))
693 bool
694 fd_state_machine::is_unchecked_fd_p (state_t s) const
696 return (s == m_unchecked_read_write
697 || s == m_unchecked_read_only
698 || s == m_unchecked_write_only);
701 bool
702 fd_state_machine::is_valid_fd_p (state_t s) const
704 return (s == m_valid_read_write
705 || s == m_valid_read_only
706 || s == m_valid_write_only);
709 enum access_mode
710 fd_state_machine::get_access_mode_from_flag (int flag) const
712 /* FIXME: this code assumes the access modes on the host and
713 target are the same, which in practice might not be the case. */
715 if ((flag & O_ACCMODE) == O_RDONLY)
717 return READ_ONLY;
719 else if ((flag & O_ACCMODE) == O_WRONLY)
721 return WRITE_ONLY;
723 return READ_WRITE;
726 bool
727 fd_state_machine::is_readonly_fd_p (state_t state) const
729 return (state == m_unchecked_read_only || state == m_valid_read_only);
732 bool
733 fd_state_machine::is_writeonly_fd_p (state_t state) const
735 return (state == m_unchecked_write_only || state == m_valid_write_only);
738 bool
739 fd_state_machine::is_closed_fd_p (state_t state) const
741 return (state == m_closed);
744 bool
745 fd_state_machine::is_constant_fd_p (state_t state) const
747 return (state == m_constant_fd);
750 fd_state_machine::state_t
751 fd_state_machine::valid_to_unchecked_state (state_t state) const
753 if (state == m_valid_read_write)
754 return m_unchecked_read_write;
755 else if (state == m_valid_write_only)
756 return m_unchecked_write_only;
757 else if (state == m_valid_read_only)
758 return m_unchecked_read_only;
759 else
760 gcc_unreachable ();
761 return NULL;
764 void
765 fd_state_machine::mark_as_valid_fd (region_model *model,
766 sm_state_map *smap,
767 const svalue *fd_sval,
768 const extrinsic_state &ext_state) const
770 smap->set_state (model, fd_sval, m_valid_read_write, NULL, ext_state);
773 bool
774 fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
775 const gimple *stmt) const
777 if (const gcall *call = dyn_cast<const gcall *> (stmt))
778 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
780 if (is_named_call_p (callee_fndecl, "open", call, 2))
782 on_open (sm_ctxt, node, stmt, call);
783 return true;
784 } // "open"
786 if (is_named_call_p (callee_fndecl, "creat", call, 2))
788 on_creat (sm_ctxt, node, stmt, call);
789 return true;
790 } // "creat"
792 if (is_named_call_p (callee_fndecl, "close", call, 1))
794 on_close (sm_ctxt, node, stmt, call);
795 return true;
796 } // "close"
798 if (is_named_call_p (callee_fndecl, "write", call, 3))
800 on_write (sm_ctxt, node, stmt, call, callee_fndecl);
801 return true;
802 } // "write"
804 if (is_named_call_p (callee_fndecl, "read", call, 3))
806 on_read (sm_ctxt, node, stmt, call, callee_fndecl);
807 return true;
808 } // "read"
810 if (is_named_call_p (callee_fndecl, "dup", call, 1))
812 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_1);
813 return true;
816 if (is_named_call_p (callee_fndecl, "dup2", call, 2))
818 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_2);
819 return true;
822 if (is_named_call_p (callee_fndecl, "dup3", call, 3))
824 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_3);
825 return true;
829 // Handle __attribute__((fd_arg))
831 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
832 "fd_arg", DIRS_READ_WRITE);
834 // Handle __attribute__((fd_arg_read))
836 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
837 "fd_arg_read", DIRS_READ);
839 // Handle __attribute__((fd_arg_write))
841 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
842 "fd_arg_write", DIRS_WRITE);
846 return false;
849 void
850 fd_state_machine::check_for_fd_attrs (
851 sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
852 const gcall *call, const tree callee_fndecl, const char *attr_name,
853 access_directions fd_attr_access_dir) const
856 tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
857 attrs = lookup_attribute (attr_name, attrs);
858 if (!attrs)
859 return;
861 if (!TREE_VALUE (attrs))
862 return;
864 auto_bitmap argmap;
866 for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
868 unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
869 bitmap_set_bit (argmap, val);
871 if (bitmap_empty_p (argmap))
872 return;
874 for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
876 tree arg = gimple_call_arg (call, arg_idx);
877 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
878 state_t state = sm_ctxt->get_state (stmt, arg);
879 bool bit_set = bitmap_bit_p (argmap, arg_idx);
880 if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
881 continue;
882 if (bit_set) // Check if arg_idx is marked by any of the file descriptor
883 // attributes
886 if (is_closed_fd_p (state))
889 sm_ctxt->warn (node, stmt, arg,
890 make_unique<fd_use_after_close>
891 (*this, diag_arg,
892 callee_fndecl, attr_name,
893 arg_idx));
894 continue;
897 if (!(is_valid_fd_p (state) || (state == m_stop)))
899 if (!is_constant_fd_p (state))
900 sm_ctxt->warn (node, stmt, arg,
901 make_unique<fd_use_without_check>
902 (*this, diag_arg,
903 callee_fndecl, attr_name,
904 arg_idx));
907 switch (fd_attr_access_dir)
909 case DIRS_READ_WRITE:
910 break;
911 case DIRS_READ:
913 if (is_writeonly_fd_p (state))
915 sm_ctxt->warn (
916 node, stmt, arg,
917 make_unique<fd_access_mode_mismatch> (*this, diag_arg,
918 DIRS_WRITE,
919 callee_fndecl,
920 attr_name,
921 arg_idx));
924 break;
925 case DIRS_WRITE:
927 if (is_readonly_fd_p (state))
929 sm_ctxt->warn (
930 node, stmt, arg,
931 make_unique<fd_access_mode_mismatch> (*this, diag_arg,
932 DIRS_READ,
933 callee_fndecl,
934 attr_name,
935 arg_idx));
938 break;
945 void
946 fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
947 const gimple *stmt, const gcall *call) const
949 tree lhs = gimple_call_lhs (call);
950 if (lhs)
952 tree arg = gimple_call_arg (call, 1);
953 enum access_mode mode = READ_WRITE;
954 if (TREE_CODE (arg) == INTEGER_CST)
956 int flag = TREE_INT_CST_LOW (arg);
957 mode = get_access_mode_from_flag (flag);
959 switch (mode)
961 case READ_ONLY:
962 sm_ctxt->on_transition (node, stmt, lhs, m_start,
963 m_unchecked_read_only);
964 break;
965 case WRITE_ONLY:
966 sm_ctxt->on_transition (node, stmt, lhs, m_start,
967 m_unchecked_write_only);
968 break;
969 default:
970 sm_ctxt->on_transition (node, stmt, lhs, m_start,
971 m_unchecked_read_write);
974 else
976 sm_ctxt->warn (node, stmt, NULL_TREE,
977 make_unique<fd_leak> (*this, NULL_TREE));
981 void
982 fd_state_machine::on_creat (sm_context *sm_ctxt, const supernode *node,
983 const gimple *stmt, const gcall *call) const
985 tree lhs = gimple_call_lhs (call);
986 if (lhs)
987 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked_write_only);
988 else
989 sm_ctxt->warn (node, stmt, NULL_TREE,
990 make_unique<fd_leak> (*this, NULL_TREE));
993 void
994 fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
995 const gimple *stmt, const gcall *call,
996 const tree callee_fndecl, enum dup kind) const
998 tree lhs = gimple_call_lhs (call);
999 tree arg_1 = gimple_call_arg (call, 0);
1000 state_t state_arg_1 = sm_ctxt->get_state (stmt, arg_1);
1001 if (state_arg_1 == m_stop)
1002 return;
1003 if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)
1004 || state_arg_1 == m_start))
1006 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl,
1007 DIRS_READ_WRITE);
1008 return;
1010 switch (kind)
1012 case DUP_1:
1013 if (lhs)
1015 if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
1016 sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
1017 else
1018 sm_ctxt->set_next_state (stmt, lhs,
1019 valid_to_unchecked_state (state_arg_1));
1021 break;
1023 case DUP_2:
1024 case DUP_3:
1025 tree arg_2 = gimple_call_arg (call, 1);
1026 state_t state_arg_2 = sm_ctxt->get_state (stmt, arg_2);
1027 tree diag_arg_2 = sm_ctxt->get_diagnostic_tree (arg_2);
1028 if (state_arg_2 == m_stop)
1029 return;
1030 /* Check if -1 was passed as second argument to dup2. */
1031 if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)
1032 || state_arg_2 == m_start))
1034 sm_ctxt->warn (
1035 node, stmt, arg_2,
1036 make_unique<fd_use_without_check> (*this, diag_arg_2,
1037 callee_fndecl));
1038 return;
1040 /* dup2 returns value of its second argument on success.But, the
1041 access mode of the returned file descriptor depends on the duplicated
1042 file descriptor i.e the first argument. */
1043 if (lhs)
1045 if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
1046 sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
1047 else
1048 sm_ctxt->set_next_state (stmt, lhs,
1049 valid_to_unchecked_state (state_arg_1));
1052 break;
1056 void
1057 fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
1058 const gimple *stmt, const gcall *call) const
1060 tree arg = gimple_call_arg (call, 0);
1061 state_t state = sm_ctxt->get_state (stmt, arg);
1062 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1064 sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
1065 sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed);
1066 sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed);
1067 sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed);
1068 sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed);
1069 sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed);
1070 sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed);
1071 sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed);
1073 if (is_closed_fd_p (state))
1075 sm_ctxt->warn (node, stmt, arg,
1076 make_unique<fd_double_close> (*this, diag_arg));
1077 sm_ctxt->set_next_state (stmt, arg, m_stop);
1080 void
1081 fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node,
1082 const gimple *stmt, const gcall *call,
1083 const tree callee_fndecl) const
1085 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ);
1087 void
1088 fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,
1089 const gimple *stmt, const gcall *call,
1090 const tree callee_fndecl) const
1092 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE);
1095 void
1096 fd_state_machine::check_for_open_fd (
1097 sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
1098 const gcall *call, const tree callee_fndecl,
1099 enum access_directions callee_fndecl_dir) const
1101 tree arg = gimple_call_arg (call, 0);
1102 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1103 state_t state = sm_ctxt->get_state (stmt, arg);
1105 if (is_closed_fd_p (state))
1107 sm_ctxt->warn (node, stmt, arg,
1108 make_unique<fd_use_after_close> (*this, diag_arg,
1109 callee_fndecl));
1112 else
1114 if (!(is_valid_fd_p (state) || state == m_start || state == m_stop))
1116 if (!is_constant_fd_p (state))
1117 sm_ctxt->warn (
1118 node, stmt, arg,
1119 make_unique<fd_use_without_check> (*this, diag_arg,
1120 callee_fndecl));
1122 switch (callee_fndecl_dir)
1124 case DIRS_READ_WRITE:
1125 break;
1126 case DIRS_READ:
1127 if (is_writeonly_fd_p (state))
1129 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1130 sm_ctxt->warn (node, stmt, arg,
1131 make_unique<fd_access_mode_mismatch> (
1132 *this, diag_arg, DIRS_WRITE, callee_fndecl));
1135 break;
1136 case DIRS_WRITE:
1138 if (is_readonly_fd_p (state))
1140 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1141 sm_ctxt->warn (node, stmt, arg,
1142 make_unique<fd_access_mode_mismatch> (
1143 *this, diag_arg, DIRS_READ, callee_fndecl));
1145 break;
1150 void
1151 fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node,
1152 const gimple *stmt, const svalue *lhs,
1153 enum tree_code op, const svalue *rhs) const
1155 if (tree cst = rhs->maybe_get_constant ())
1157 if (TREE_CODE (cst) == INTEGER_CST)
1159 int val = TREE_INT_CST_LOW (cst);
1160 if (val == -1)
1162 if (op == NE_EXPR)
1163 make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
1165 else if (op == EQ_EXPR)
1166 make_invalid_transitions_on_condition (sm_ctxt, node, stmt,
1167 lhs);
1172 if (rhs->all_zeroes_p ())
1174 if (op == GE_EXPR)
1175 make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
1176 else if (op == LT_EXPR)
1177 make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
1181 void
1182 fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt,
1183 const supernode *node,
1184 const gimple *stmt,
1185 const svalue *lhs) const
1187 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write,
1188 m_valid_read_write);
1189 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only,
1190 m_valid_read_only);
1191 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only,
1192 m_valid_write_only);
1195 void
1196 fd_state_machine::make_invalid_transitions_on_condition (
1197 sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
1198 const svalue *lhs) const
1200 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid);
1201 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid);
1202 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid);
1205 bool
1206 fd_state_machine::can_purge_p (state_t s) const
1208 if (is_unchecked_fd_p (s) || is_valid_fd_p (s))
1209 return false;
1210 else
1211 return true;
1214 std::unique_ptr<pending_diagnostic>
1215 fd_state_machine::on_leak (tree var) const
1217 return make_unique<fd_leak> (*this, var);
1219 } // namespace
1221 state_machine *
1222 make_fd_state_machine (logger *logger)
1224 return new fd_state_machine (logger);
1227 /* Specialcase hook for handling pipe, for use by
1228 region_model::impl_call_pipe::success::update_model. */
1230 void
1231 region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
1233 if (!ctxt)
1234 return;
1235 const extrinsic_state *ext_state = ctxt->get_ext_state ();
1236 if (!ext_state)
1237 return;
1239 sm_state_map *smap;
1240 const state_machine *sm;
1241 unsigned sm_idx;
1242 if (!ctxt->get_fd_map (&smap, &sm, &sm_idx))
1243 return;
1245 gcc_assert (smap);
1246 gcc_assert (sm);
1248 const fd_state_machine &fd_sm = (const fd_state_machine &)*sm;
1250 fd_sm.mark_as_valid_fd (this, smap, sval, *ext_state);
1253 } // namespace ana
1255 #endif // ENABLE_ANALYZER