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)
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/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
25 #include "make-unique.h"
28 #include "basic-block.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"
42 #include "analyzer/call-string.h"
43 #include "analyzer/program-point.h"
44 #include "analyzer/store.h"
45 #include "analyzer/region-model.h"
47 #include "analyzer/program-state.h"
55 /* An enum for distinguishing between three different access modes. */
64 enum access_directions
71 /* An enum for distinguishing between dup, dup2 and dup3. */
79 class fd_state_machine
: public state_machine
82 fd_state_machine (logger
*logger
);
85 inherited_state_p () const final override
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
);
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
,
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
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). */
156 /* State for a file descriptor that has been closed. */
159 /* State for a file descriptor that we do not want to track anymore . */
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
,
181 const svalue
*lhs
) const;
182 void make_invalid_transitions_on_condition (sm_context
*sm_ctxt
,
183 const supernode
*node
,
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
199 fd_diagnostic (const fd_state_machine
&sm
, tree arg
) : m_sm (sm
), m_arg (arg
)
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
);
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
))
236 return change
.formatted_print (
237 "assuming %qE is a valid file descriptor (>= 0)", change
.m_expr
);
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
)
246 return change
.formatted_print (
247 "assuming %qE is an invalid file descriptor (< 0)",
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 ();
271 const fd_state_machine
&m_sm
;
275 class fd_param_diagnostic
: public fd_diagnostic
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)
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
300 ? (strcmp (m_attr_name
, sub_other
.m_attr_name
) == 0)
305 inform_filedescriptor_attribute (access_directions 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);
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);
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);
333 tree m_callee_fndecl
;
334 const char *m_attr_name
;
335 /* ARG_IDX is 0-based. */
339 class fd_leak
: public fd_diagnostic
342 fd_leak (const fd_state_machine
&sm
, tree arg
) : fd_diagnostic (sm
, arg
) {}
345 get_kind () const final override
351 get_controlling_option () const final override
353 return OPT_Wanalyzer_fd_leak
;
357 emit (rich_location
*rich_loc
) final override
359 /*CWE-775: Missing Release of File Descriptor or Handle after Effective
362 diagnostic_metadata m
;
365 return warning_meta (rich_loc
, m
, get_controlling_option (),
366 "leak of file descriptor %qE", m_arg
);
368 return warning_meta (rich_loc
, m
, get_controlling_option (),
369 "leak of file descriptor");
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
);
385 describe_final_event (const evdesc::final_event
&ev
) final override
387 if (m_open_event
.known_p ())
390 return ev
.formatted_print ("%qE leaks here; was opened at %@",
391 ev
.m_expr
, &m_open_event
);
393 return ev
.formatted_print ("leaks here; was opened at %@",
399 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
401 return ev
.formatted_print ("leaks here");
406 diagnostic_event_id_t m_open_event
;
409 class fd_access_mode_mismatch
: public fd_param_diagnostic
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
,
416 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
),
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
)
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
;
442 emit (rich_location
*rich_loc
) final override
448 warned
= warning_at (rich_loc
, get_controlling_option (),
449 "%qE on read-only file descriptor %qE",
450 m_callee_fndecl
, m_arg
);
453 warned
= warning_at (rich_loc
, get_controlling_option (),
454 "%qE on write-only file descriptor %qE",
455 m_callee_fndecl
, m_arg
);
461 inform_filedescriptor_attribute (m_fd_dir
);
466 describe_final_event (const evdesc::final_event
&ev
) final override
471 return ev
.formatted_print ("%qE on read-only file descriptor %qE",
472 m_callee_fndecl
, m_arg
);
474 return ev
.formatted_print ("%qE on write-only file descriptor %qE",
475 m_callee_fndecl
, m_arg
);
482 enum access_directions m_fd_dir
;
485 class fd_double_close
: public fd_diagnostic
488 fd_double_close (const fd_state_machine
&sm
, tree arg
) : fd_diagnostic (sm
, arg
)
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
;
504 emit (rich_location
*rich_loc
) final override
506 diagnostic_metadata m
;
507 // CWE-1341: Multiple Releases of Same Resource or Handle
509 return warning_meta (rich_loc
, m
, get_controlling_option (),
510 "double %<close%> of file descriptor %qE", m_arg
);
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
);
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");
537 diagnostic_event_id_t m_first_close_event
;
540 class fd_use_after_close
: public fd_param_diagnostic
543 fd_use_after_close (const fd_state_machine
&sm
, tree arg
,
544 const tree callee_fndecl
, const char *attr_name
,
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
)
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
;
569 emit (rich_location
*rich_loc
) final override
572 warned
= warning_at (rich_loc
, get_controlling_option (),
573 "%qE on closed file descriptor %qE", m_callee_fndecl
,
576 inform_filedescriptor_attribute (DIRS_READ_WRITE
);
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
);
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
);
603 return ev
.formatted_print ("%qE on closed file descriptor %qE",
604 m_callee_fndecl
, m_arg
);
608 diagnostic_event_id_t m_first_close_event
;
611 class fd_use_without_check
: public fd_param_diagnostic
614 fd_use_without_check (const fd_state_machine
&sm
, tree arg
,
615 const tree callee_fndecl
, const char *attr_name
,
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
)
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
;
640 emit (rich_location
*rich_loc
) final override
643 warned
= warning_at (rich_loc
, get_controlling_option (),
644 "%qE on possibly invalid file descriptor %qE",
645 m_callee_fndecl
, m_arg
);
647 inform_filedescriptor_attribute (DIRS_READ_WRITE
);
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
);
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
);
671 return ev
.formatted_print ("%qE could be invalid", m_arg
);
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"))
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
);
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
);
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
)
719 else if ((flag
& O_ACCMODE
) == O_WRONLY
)
727 fd_state_machine::is_readonly_fd_p (state_t state
) const
729 return (state
== m_unchecked_read_only
|| state
== m_valid_read_only
);
733 fd_state_machine::is_writeonly_fd_p (state_t state
) const
735 return (state
== m_unchecked_write_only
|| state
== m_valid_write_only
);
739 fd_state_machine::is_closed_fd_p (state_t state
) const
741 return (state
== m_closed
);
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
;
765 fd_state_machine::mark_as_valid_fd (region_model
*model
,
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
);
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
);
786 if (is_named_call_p (callee_fndecl
, "creat", call
, 2))
788 on_creat (sm_ctxt
, node
, stmt
, call
);
792 if (is_named_call_p (callee_fndecl
, "close", call
, 1))
794 on_close (sm_ctxt
, node
, stmt
, call
);
798 if (is_named_call_p (callee_fndecl
, "write", call
, 3))
800 on_write (sm_ctxt
, node
, stmt
, call
, callee_fndecl
);
804 if (is_named_call_p (callee_fndecl
, "read", call
, 3))
806 on_read (sm_ctxt
, node
, stmt
, call
, callee_fndecl
);
810 if (is_named_call_p (callee_fndecl
, "dup", call
, 1))
812 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_1
);
816 if (is_named_call_p (callee_fndecl
, "dup2", call
, 2))
818 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_2
);
822 if (is_named_call_p (callee_fndecl
, "dup3", call
, 3))
824 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_3
);
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
);
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
);
861 if (!TREE_VALUE (attrs
))
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
))
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
)
882 if (bit_set
) // Check if arg_idx is marked by any of the file descriptor
886 if (is_closed_fd_p (state
))
889 sm_ctxt
->warn (node
, stmt
, arg
,
890 make_unique
<fd_use_after_close
>
892 callee_fndecl
, attr_name
,
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
>
903 callee_fndecl
, attr_name
,
907 switch (fd_attr_access_dir
)
909 case DIRS_READ_WRITE
:
913 if (is_writeonly_fd_p (state
))
917 make_unique
<fd_access_mode_mismatch
> (*this, diag_arg
,
927 if (is_readonly_fd_p (state
))
931 make_unique
<fd_access_mode_mismatch
> (*this, diag_arg
,
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
);
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
);
962 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
,
963 m_unchecked_read_only
);
966 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
,
967 m_unchecked_write_only
);
970 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
,
971 m_unchecked_read_write
);
976 sm_ctxt
->warn (node
, stmt
, NULL_TREE
,
977 make_unique
<fd_leak
> (*this, NULL_TREE
));
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
);
987 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_unchecked_write_only
);
989 sm_ctxt
->warn (node
, stmt
, NULL_TREE
,
990 make_unique
<fd_leak
> (*this, NULL_TREE
));
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
)
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
,
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
);
1018 sm_ctxt
->set_next_state (stmt
, lhs
,
1019 valid_to_unchecked_state (state_arg_1
));
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
)
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
))
1036 make_unique
<fd_use_without_check
> (*this, diag_arg_2
,
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. */
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
);
1048 sm_ctxt
->set_next_state (stmt
, lhs
,
1049 valid_to_unchecked_state (state_arg_1
));
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
);
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
);
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
);
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
,
1114 if (!(is_valid_fd_p (state
) || state
== m_start
|| state
== m_stop
))
1116 if (!is_constant_fd_p (state
))
1119 make_unique
<fd_use_without_check
> (*this, diag_arg
,
1122 switch (callee_fndecl_dir
)
1124 case DIRS_READ_WRITE
:
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
));
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
));
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
);
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
,
1172 if (rhs
->all_zeroes_p ())
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
);
1182 fd_state_machine::make_valid_transitions_on_condition (sm_context
*sm_ctxt
,
1183 const supernode
*node
,
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
,
1191 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_write_only
,
1192 m_valid_write_only
);
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
);
1206 fd_state_machine::can_purge_p (state_t s
) const
1208 if (is_unchecked_fd_p (s
) || is_valid_fd_p (s
))
1214 std::unique_ptr
<pending_diagnostic
>
1215 fd_state_machine::on_leak (tree var
) const
1217 return make_unique
<fd_leak
> (*this, var
);
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. */
1231 region_model::mark_as_valid_fd (const svalue
*sval
, region_model_context
*ctxt
)
1235 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
1240 const state_machine
*sm
;
1242 if (!ctxt
->get_fd_map (&smap
, &sm
, &sm_idx
))
1248 const fd_state_machine
&fd_sm
= (const fd_state_machine
&)*sm
;
1250 fd_sm
.mark_as_valid_fd (this, smap
, sval
, *ext_state
);
1255 #endif // ENABLE_ANALYZER