1 /* A state machine for detecting misuses of POSIX file descriptor APIs.
2 Copyright (C) 2019-2024 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
23 #define INCLUDE_VECTOR
25 #include "coretypes.h"
26 #include "make-unique.h"
29 #include "basic-block.h"
32 #include "diagnostic-path.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"
48 #include "analyzer/supergraph.h"
49 #include "analyzer/analyzer-language.h"
50 #include "analyzer/call-details.h"
51 #include "analyzer/call-info.h"
59 /* An enum for distinguishing between three different access modes. */
68 enum access_directions
75 /* An enum for distinguishing between dup, dup2 and dup3. */
83 /* Enum for use by -Wanalyzer-fd-phase-mismatch. */
87 EXPECTED_PHASE_CAN_TRANSFER
, /* can "read"/"write". */
88 EXPECTED_PHASE_CAN_BIND
,
89 EXPECTED_PHASE_CAN_LISTEN
,
90 EXPECTED_PHASE_CAN_ACCEPT
,
91 EXPECTED_PHASE_CAN_CONNECT
94 class fd_state_machine
: public state_machine
97 fd_state_machine (logger
*logger
);
100 inherited_state_p () const final override
105 state_machine::state_t
106 get_default_state (const svalue
*sval
) const final override
108 if (tree cst
= sval
->maybe_get_constant ())
110 if (TREE_CODE (cst
) == INTEGER_CST
)
112 int val
= TREE_INT_CST_LOW (cst
);
114 return m_constant_fd
;
122 bool on_stmt (sm_context
&sm_ctxt
, const supernode
*node
,
123 const gimple
*stmt
) const final override
;
125 void on_condition (sm_context
&sm_ctxt
, const supernode
*node
,
126 const gimple
*stmt
, const svalue
*lhs
, const tree_code op
,
127 const svalue
*rhs
) const final override
;
129 bool can_purge_p (state_t s
) const final override
;
130 std::unique_ptr
<pending_diagnostic
> on_leak (tree var
) const final override
;
132 bool is_unchecked_fd_p (state_t s
) const;
133 bool is_valid_fd_p (state_t s
) const;
134 bool is_socket_fd_p (state_t s
) const;
135 bool is_datagram_socket_fd_p (state_t s
) const;
136 bool is_stream_socket_fd_p (state_t s
) const;
137 bool is_closed_fd_p (state_t s
) const;
138 bool is_constant_fd_p (state_t s
) const;
139 bool is_readonly_fd_p (state_t s
) const;
140 bool is_writeonly_fd_p (state_t s
) const;
141 enum access_mode
get_access_mode_from_flag (int flag
) const;
142 /* Function for one-to-one correspondence between valid
143 and unchecked states. */
144 state_t
valid_to_unchecked_state (state_t state
) const;
146 void mark_as_valid_fd (region_model
*model
,
148 const svalue
*fd_sval
,
149 const extrinsic_state
&ext_state
) const;
151 bool on_socket (const call_details
&cd
,
154 const extrinsic_state
&ext_state
) const;
155 bool on_bind (const call_details
&cd
,
158 const extrinsic_state
&ext_state
) const;
159 bool on_listen (const call_details
&cd
,
162 const extrinsic_state
&ext_state
) const;
163 bool on_accept (const call_details
&cd
,
166 const extrinsic_state
&ext_state
) const;
167 bool on_connect (const call_details
&cd
,
170 const extrinsic_state
&ext_state
) const;
172 /* State for a constant file descriptor (>= 0) */
173 state_t m_constant_fd
;
175 /* States representing a file descriptor that hasn't yet been
176 checked for validity after opening, for three different
178 state_t m_unchecked_read_write
;
180 state_t m_unchecked_read_only
;
182 state_t m_unchecked_write_only
;
184 /* States for representing a file descriptor that is known to be valid (>=
185 0), for three different access modes. */
186 state_t m_valid_read_write
;
188 state_t m_valid_read_only
;
190 state_t m_valid_write_only
;
192 /* State for a file descriptor that is known to be invalid (< 0). */
195 /* State for a file descriptor that has been closed. */
198 /* States for FDs relating to socket APIs. */
200 /* Result of successful "socket" with SOCK_DGRAM. */
201 state_t m_new_datagram_socket
;
202 /* Result of successful "socket" with SOCK_STREAM. */
203 state_t m_new_stream_socket
;
204 /* Result of successful "socket" with unknown type. */
205 state_t m_new_unknown_socket
;
207 /* The above after a successful call to "bind". */
208 state_t m_bound_datagram_socket
;
209 state_t m_bound_stream_socket
;
210 state_t m_bound_unknown_socket
;
212 /* A bound socket after a successful call to "listen" (stream or unknown). */
213 state_t m_listening_stream_socket
;
215 /* (i) the new FD as a result of a succesful call to "accept" on a
216 listening socket (via a passive open), or
217 (ii) an active socket after a successful call to "connect"
218 (via an active open). */
219 state_t m_connected_stream_socket
;
221 /* State for a file descriptor that we do not want to track anymore . */
224 /* Stashed constant values from the frontend. These could be NULL. */
232 void on_open (sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
233 const gcall
*call
) const;
234 void on_creat (sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
235 const gcall
*call
) const;
236 void on_close (sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
237 const gcall
*call
) const;
238 void on_read (sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
239 const gcall
*call
, const tree callee_fndecl
) const;
240 void on_write (sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
241 const gcall
*call
, const tree callee_fndecl
) const;
242 void check_for_open_fd (sm_context
&sm_ctxt
, const supernode
*node
,
243 const gimple
*stmt
, const gcall
*call
,
244 const tree callee_fndecl
,
245 enum access_directions access_fn
) const;
247 void make_valid_transitions_on_condition (sm_context
&sm_ctxt
,
248 const supernode
*node
,
250 const svalue
*lhs
) const;
251 void make_invalid_transitions_on_condition (sm_context
&sm_ctxt
,
252 const supernode
*node
,
254 const svalue
*lhs
) const;
255 void check_for_fd_attrs (sm_context
&sm_ctxt
, const supernode
*node
,
256 const gimple
*stmt
, const gcall
*call
,
257 const tree callee_fndecl
, const char *attr_name
,
258 access_directions fd_attr_access_dir
) const;
259 void check_for_dup (sm_context
&sm_ctxt
, const supernode
*node
,
260 const gimple
*stmt
, const gcall
*call
, const tree callee_fndecl
,
261 enum dup kind
) const;
263 state_t
get_state_for_socket_type (const svalue
*socket_type_sval
) const;
265 bool check_for_socket_fd (const call_details
&cd
,
268 const svalue
*fd_sval
,
269 const supernode
*node
,
271 bool *complained
= NULL
) const;
272 bool check_for_new_socket_fd (const call_details
&cd
,
275 const svalue
*fd_sval
,
276 const supernode
*node
,
278 enum expected_phase expected_phase
) const;
281 /* Base diagnostic class relative to fd_state_machine. */
282 class fd_diagnostic
: public pending_diagnostic
285 fd_diagnostic (const fd_state_machine
&sm
, tree arg
) : m_sm (sm
), m_arg (arg
)
290 subclass_equal_p (const pending_diagnostic
&base_other
) const override
292 return same_tree_p (m_arg
, ((const fd_diagnostic
&)base_other
).m_arg
);
296 describe_state_change (const evdesc::state_change
&change
) override
298 if (change
.m_old_state
== m_sm
.get_start_state ())
300 if (change
.m_new_state
== m_sm
.m_unchecked_read_write
301 || change
.m_new_state
== m_sm
.m_valid_read_write
)
302 return change
.formatted_print ("opened here as read-write");
304 if (change
.m_new_state
== m_sm
.m_unchecked_read_only
305 || change
.m_new_state
== m_sm
.m_valid_read_only
)
306 return change
.formatted_print ("opened here as read-only");
308 if (change
.m_new_state
== m_sm
.m_unchecked_write_only
309 || change
.m_new_state
== m_sm
.m_valid_write_only
)
310 return change
.formatted_print ("opened here as write-only");
312 if (change
.m_new_state
== m_sm
.m_new_datagram_socket
)
313 return change
.formatted_print ("datagram socket created here");
315 if (change
.m_new_state
== m_sm
.m_new_stream_socket
)
316 return change
.formatted_print ("stream socket created here");
318 if (change
.m_new_state
== m_sm
.m_new_unknown_socket
319 || change
.m_new_state
== m_sm
.m_connected_stream_socket
)
320 return change
.formatted_print ("socket created here");
323 if (change
.m_new_state
== m_sm
.m_bound_datagram_socket
)
324 return change
.formatted_print ("datagram socket bound here");
326 if (change
.m_new_state
== m_sm
.m_bound_stream_socket
)
327 return change
.formatted_print ("stream socket bound here");
329 if (change
.m_new_state
== m_sm
.m_bound_unknown_socket
330 || change
.m_new_state
== m_sm
.m_connected_stream_socket
)
331 return change
.formatted_print ("socket bound here");
333 if (change
.m_new_state
== m_sm
.m_listening_stream_socket
)
334 return change
.formatted_print
335 ("stream socket marked as passive here via %qs", "listen");
337 if (change
.m_new_state
== m_sm
.m_closed
)
338 return change
.formatted_print ("closed here");
340 if (m_sm
.is_unchecked_fd_p (change
.m_old_state
)
341 && m_sm
.is_valid_fd_p (change
.m_new_state
))
344 return change
.formatted_print (
345 "assuming %qE is a valid file descriptor (>= 0)", change
.m_expr
);
347 return change
.formatted_print ("assuming a valid file descriptor");
350 if (m_sm
.is_unchecked_fd_p (change
.m_old_state
)
351 && change
.m_new_state
== m_sm
.m_invalid
)
354 return change
.formatted_print (
355 "assuming %qE is an invalid file descriptor (< 0)",
358 return change
.formatted_print ("assuming an invalid file descriptor");
361 return label_text ();
364 diagnostic_event::meaning
365 get_meaning_for_state_change (
366 const evdesc::state_change
&change
) const final override
368 if (change
.m_old_state
== m_sm
.get_start_state ()
369 && (m_sm
.is_unchecked_fd_p (change
.m_new_state
)
370 || change
.m_new_state
== m_sm
.m_new_datagram_socket
371 || change
.m_new_state
== m_sm
.m_new_stream_socket
372 || change
.m_new_state
== m_sm
.m_new_unknown_socket
))
373 return diagnostic_event::meaning (diagnostic_event::VERB_acquire
,
374 diagnostic_event::NOUN_resource
);
375 if (change
.m_new_state
== m_sm
.m_closed
)
376 return diagnostic_event::meaning (diagnostic_event::VERB_release
,
377 diagnostic_event::NOUN_resource
);
378 return diagnostic_event::meaning ();
382 const fd_state_machine
&m_sm
;
386 class fd_param_diagnostic
: public fd_diagnostic
389 fd_param_diagnostic (const fd_state_machine
&sm
, tree arg
, tree callee_fndecl
,
390 const char *attr_name
, int arg_idx
)
391 : fd_diagnostic (sm
, arg
), m_callee_fndecl (callee_fndecl
),
392 m_attr_name (attr_name
), m_arg_idx (arg_idx
)
396 fd_param_diagnostic (const fd_state_machine
&sm
, tree arg
, tree callee_fndecl
)
397 : fd_diagnostic (sm
, arg
), m_callee_fndecl (callee_fndecl
),
398 m_attr_name (NULL
), m_arg_idx (-1)
403 subclass_equal_p (const pending_diagnostic
&base_other
) const override
405 const fd_param_diagnostic
&sub_other
406 = (const fd_param_diagnostic
&)base_other
;
407 return (same_tree_p (m_arg
, sub_other
.m_arg
)
408 && same_tree_p (m_callee_fndecl
, sub_other
.m_callee_fndecl
)
409 && m_arg_idx
== sub_other
.m_arg_idx
411 ? (strcmp (m_attr_name
, sub_other
.m_attr_name
) == 0)
416 inform_filedescriptor_attribute (access_directions fd_dir
)
422 case DIRS_READ_WRITE
:
423 inform (DECL_SOURCE_LOCATION (m_callee_fndecl
),
424 "argument %d of %qD must be an open file descriptor, due to "
425 "%<__attribute__((%s(%d)))%>",
426 m_arg_idx
+ 1, m_callee_fndecl
, m_attr_name
, m_arg_idx
+ 1);
429 inform (DECL_SOURCE_LOCATION (m_callee_fndecl
),
430 "argument %d of %qD must be a readable file descriptor, due "
431 "to %<__attribute__((%s(%d)))%>",
432 m_arg_idx
+ 1, m_callee_fndecl
, m_attr_name
, m_arg_idx
+ 1);
435 inform (DECL_SOURCE_LOCATION (m_callee_fndecl
),
436 "argument %d of %qD must be a writable file descriptor, due "
437 "to %<__attribute__((%s(%d)))%>",
438 m_arg_idx
+ 1, m_callee_fndecl
, m_attr_name
, m_arg_idx
+ 1);
444 tree m_callee_fndecl
;
445 const char *m_attr_name
;
446 /* ARG_IDX is 0-based. */
450 class fd_leak
: public fd_diagnostic
453 fd_leak (const fd_state_machine
&sm
, tree arg
) : fd_diagnostic (sm
, arg
) {}
456 get_kind () const final override
462 get_controlling_option () const final override
464 return OPT_Wanalyzer_fd_leak
;
468 emit (diagnostic_emission_context
&ctxt
) final override
470 /*CWE-775: Missing Release of File Descriptor or Handle after Effective
475 return ctxt
.warn ("leak of file descriptor %qE", m_arg
);
477 return ctxt
.warn ("leak of file descriptor");
481 describe_state_change (const evdesc::state_change
&change
) final override
483 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
485 m_open_event
= change
.m_event_id
;
486 return label_text::borrow ("opened here");
489 return fd_diagnostic::describe_state_change (change
);
493 describe_final_event (const evdesc::final_event
&ev
) final override
495 if (m_open_event
.known_p ())
498 return ev
.formatted_print ("%qE leaks here; was opened at %@",
499 ev
.m_expr
, &m_open_event
);
501 return ev
.formatted_print ("leaks here; was opened at %@",
507 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
509 return ev
.formatted_print ("leaks here");
514 diagnostic_event_id_t m_open_event
;
517 class fd_access_mode_mismatch
: public fd_param_diagnostic
520 fd_access_mode_mismatch (const fd_state_machine
&sm
, tree arg
,
521 enum access_directions fd_dir
,
522 const tree callee_fndecl
, const char *attr_name
,
524 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
),
530 fd_access_mode_mismatch (const fd_state_machine
&sm
, tree arg
,
531 enum access_directions fd_dir
,
532 const tree callee_fndecl
)
533 : fd_param_diagnostic (sm
, arg
, callee_fndecl
), m_fd_dir (fd_dir
)
538 get_kind () const final override
540 return "fd_access_mode_mismatch";
544 get_controlling_option () const final override
546 return OPT_Wanalyzer_fd_access_mode_mismatch
;
550 emit (diagnostic_emission_context
&ctxt
) final override
556 warned
= ctxt
.warn ("%qE on read-only file descriptor %qE",
557 m_callee_fndecl
, m_arg
);
560 warned
= ctxt
.warn ("%qE on write-only file descriptor %qE",
561 m_callee_fndecl
, m_arg
);
567 inform_filedescriptor_attribute (m_fd_dir
);
572 describe_final_event (const evdesc::final_event
&ev
) final override
577 return ev
.formatted_print ("%qE on read-only file descriptor %qE",
578 m_callee_fndecl
, m_arg
);
580 return ev
.formatted_print ("%qE on write-only file descriptor %qE",
581 m_callee_fndecl
, m_arg
);
588 enum access_directions m_fd_dir
;
591 class fd_double_close
: public fd_diagnostic
594 fd_double_close (const fd_state_machine
&sm
, tree arg
) : fd_diagnostic (sm
, arg
)
599 get_kind () const final override
601 return "fd_double_close";
605 get_controlling_option () const final override
607 return OPT_Wanalyzer_fd_double_close
;
610 emit (diagnostic_emission_context
&ctxt
) final override
612 // CWE-1341: Multiple Releases of Same Resource or Handle
614 return ctxt
.warn ("double %<close%> of file descriptor %qE", m_arg
);
618 describe_state_change (const evdesc::state_change
&change
) override
620 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
621 return label_text::borrow ("opened here");
623 if (change
.m_new_state
== m_sm
.m_closed
)
625 m_first_close_event
= change
.m_event_id
;
626 return change
.formatted_print ("first %qs here", "close");
628 return fd_diagnostic::describe_state_change (change
);
632 describe_final_event (const evdesc::final_event
&ev
) final override
634 if (m_first_close_event
.known_p ())
635 return ev
.formatted_print ("second %qs here; first %qs was at %@",
636 "close", "close", &m_first_close_event
);
637 return ev
.formatted_print ("second %qs here", "close");
641 diagnostic_event_id_t m_first_close_event
;
644 class fd_use_after_close
: public fd_param_diagnostic
647 fd_use_after_close (const fd_state_machine
&sm
, tree arg
,
648 const tree callee_fndecl
, const char *attr_name
,
650 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
)
654 fd_use_after_close (const fd_state_machine
&sm
, tree arg
,
655 const tree callee_fndecl
)
656 : fd_param_diagnostic (sm
, arg
, callee_fndecl
)
661 get_kind () const final override
663 return "fd_use_after_close";
667 get_controlling_option () const final override
669 return OPT_Wanalyzer_fd_use_after_close
;
673 emit (diagnostic_emission_context
&ctxt
) final override
675 bool warned
= ctxt
.warn ("%qE on closed file descriptor %qE",
676 m_callee_fndecl
, m_arg
);
678 inform_filedescriptor_attribute (DIRS_READ_WRITE
);
683 describe_state_change (const evdesc::state_change
&change
) override
685 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
686 return label_text::borrow ("opened here");
688 if (change
.m_new_state
== m_sm
.m_closed
)
690 m_first_close_event
= change
.m_event_id
;
691 return change
.formatted_print ("closed here");
694 return fd_diagnostic::describe_state_change (change
);
698 describe_final_event (const evdesc::final_event
&ev
) final override
700 if (m_first_close_event
.known_p ())
701 return ev
.formatted_print (
702 "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl
,
703 m_arg
, "close", &m_first_close_event
);
705 return ev
.formatted_print ("%qE on closed file descriptor %qE",
706 m_callee_fndecl
, m_arg
);
710 diagnostic_event_id_t m_first_close_event
;
713 class fd_use_without_check
: public fd_param_diagnostic
716 fd_use_without_check (const fd_state_machine
&sm
, tree arg
,
717 const tree callee_fndecl
, const char *attr_name
,
719 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
)
723 fd_use_without_check (const fd_state_machine
&sm
, tree arg
,
724 const tree callee_fndecl
)
725 : fd_param_diagnostic (sm
, arg
, callee_fndecl
)
730 get_kind () const final override
732 return "fd_use_without_check";
736 get_controlling_option () const final override
738 return OPT_Wanalyzer_fd_use_without_check
;
742 emit (diagnostic_emission_context
&ctxt
) final override
744 bool warned
= ctxt
.warn ("%qE on possibly invalid file descriptor %qE",
745 m_callee_fndecl
, m_arg
);
747 inform_filedescriptor_attribute (DIRS_READ_WRITE
);
752 describe_state_change (const evdesc::state_change
&change
) override
754 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
756 m_first_open_event
= change
.m_event_id
;
757 return label_text::borrow ("opened here");
760 return fd_diagnostic::describe_state_change (change
);
764 describe_final_event (const evdesc::final_event
&ev
) final override
766 if (m_first_open_event
.known_p ())
767 return ev
.formatted_print (
768 "%qE could be invalid: unchecked value from %@", m_arg
,
769 &m_first_open_event
);
771 return ev
.formatted_print ("%qE could be invalid", m_arg
);
775 diagnostic_event_id_t m_first_open_event
;
778 /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch. */
780 class fd_phase_mismatch
: public fd_param_diagnostic
783 fd_phase_mismatch (const fd_state_machine
&sm
, tree arg
,
784 const tree callee_fndecl
,
785 state_machine::state_t actual_state
,
786 enum expected_phase expected_phase
)
787 : fd_param_diagnostic (sm
, arg
, callee_fndecl
),
788 m_actual_state (actual_state
),
789 m_expected_phase (expected_phase
)
791 gcc_assert (m_sm
.is_socket_fd_p (actual_state
));
792 switch (expected_phase
)
794 case EXPECTED_PHASE_CAN_TRANSFER
:
795 gcc_assert (actual_state
== m_sm
.m_new_stream_socket
796 || actual_state
== m_sm
.m_bound_stream_socket
797 || actual_state
== m_sm
.m_listening_stream_socket
);
799 case EXPECTED_PHASE_CAN_BIND
:
800 gcc_assert (actual_state
== m_sm
.m_bound_datagram_socket
801 || actual_state
== m_sm
.m_bound_stream_socket
802 || actual_state
== m_sm
.m_bound_unknown_socket
803 || actual_state
== m_sm
.m_connected_stream_socket
804 || actual_state
== m_sm
.m_listening_stream_socket
);
806 case EXPECTED_PHASE_CAN_LISTEN
:
807 gcc_assert (actual_state
== m_sm
.m_new_stream_socket
808 || actual_state
== m_sm
.m_new_unknown_socket
809 || actual_state
== m_sm
.m_connected_stream_socket
);
811 case EXPECTED_PHASE_CAN_ACCEPT
:
812 gcc_assert (actual_state
== m_sm
.m_new_stream_socket
813 || actual_state
== m_sm
.m_new_unknown_socket
814 || actual_state
== m_sm
.m_bound_stream_socket
815 || actual_state
== m_sm
.m_bound_unknown_socket
816 || actual_state
== m_sm
.m_connected_stream_socket
);
818 case EXPECTED_PHASE_CAN_CONNECT
:
819 gcc_assert (actual_state
== m_sm
.m_bound_datagram_socket
820 || actual_state
== m_sm
.m_bound_stream_socket
821 || actual_state
== m_sm
.m_bound_unknown_socket
822 || actual_state
== m_sm
.m_listening_stream_socket
823 || actual_state
== m_sm
.m_connected_stream_socket
);
829 get_kind () const final override
831 return "fd_phase_mismatch";
835 subclass_equal_p (const pending_diagnostic
&base_other
) const final override
837 const fd_phase_mismatch
&sub_other
= (const fd_phase_mismatch
&)base_other
;
838 if (!fd_param_diagnostic ::subclass_equal_p (sub_other
))
840 return (m_actual_state
== sub_other
.m_actual_state
841 && m_expected_phase
== sub_other
.m_expected_phase
);
845 get_controlling_option () const final override
847 return OPT_Wanalyzer_fd_phase_mismatch
;
851 emit (diagnostic_emission_context
&ctxt
) final override
853 /* CWE-666: Operation on Resource in Wrong Phase of Lifetime. */
855 return ctxt
.warn ("%qE on file descriptor %qE in wrong phase",
856 m_callee_fndecl
, m_arg
);
860 describe_final_event (const evdesc::final_event
&ev
) final override
862 switch (m_expected_phase
)
864 case EXPECTED_PHASE_CAN_TRANSFER
:
866 if (m_actual_state
== m_sm
.m_new_stream_socket
)
867 return ev
.formatted_print
868 ("%qE expects a stream socket to be connected via %qs"
869 " but %qE has not yet been bound",
870 m_callee_fndecl
, "accept", m_arg
);
871 if (m_actual_state
== m_sm
.m_bound_stream_socket
)
872 return ev
.formatted_print
873 ("%qE expects a stream socket to be connected via %qs"
874 " but %qE is not yet listening",
875 m_callee_fndecl
, "accept", m_arg
);
876 if (m_actual_state
== m_sm
.m_listening_stream_socket
)
877 return ev
.formatted_print
878 ("%qE expects a stream socket to be connected via"
879 " the return value of %qs"
880 " but %qE is listening; wrong file descriptor?",
881 m_callee_fndecl
, "accept", m_arg
);
884 case EXPECTED_PHASE_CAN_BIND
:
886 if (m_actual_state
== m_sm
.m_bound_datagram_socket
887 || m_actual_state
== m_sm
.m_bound_stream_socket
888 || m_actual_state
== m_sm
.m_bound_unknown_socket
)
889 return ev
.formatted_print
890 ("%qE expects a new socket file descriptor"
891 " but %qE has already been bound",
892 m_callee_fndecl
, m_arg
);
893 if (m_actual_state
== m_sm
.m_connected_stream_socket
)
894 return ev
.formatted_print
895 ("%qE expects a new socket file descriptor"
896 " but %qE is already connected",
897 m_callee_fndecl
, m_arg
);
898 if (m_actual_state
== m_sm
.m_listening_stream_socket
)
899 return ev
.formatted_print
900 ("%qE expects a new socket file descriptor"
901 " but %qE is already listening",
902 m_callee_fndecl
, m_arg
);
905 case EXPECTED_PHASE_CAN_LISTEN
:
907 if (m_actual_state
== m_sm
.m_new_stream_socket
908 || m_actual_state
== m_sm
.m_new_unknown_socket
)
909 return ev
.formatted_print
910 ("%qE expects a bound stream socket file descriptor"
911 " but %qE has not yet been bound",
912 m_callee_fndecl
, m_arg
);
913 if (m_actual_state
== m_sm
.m_connected_stream_socket
)
914 return ev
.formatted_print
915 ("%qE expects a bound stream socket file descriptor"
916 " but %qE is connected",
917 m_callee_fndecl
, m_arg
);
920 case EXPECTED_PHASE_CAN_ACCEPT
:
922 if (m_actual_state
== m_sm
.m_new_stream_socket
923 || m_actual_state
== m_sm
.m_new_unknown_socket
)
924 return ev
.formatted_print
925 ("%qE expects a listening stream socket file descriptor"
926 " but %qE has not yet been bound",
927 m_callee_fndecl
, m_arg
);
928 if (m_actual_state
== m_sm
.m_bound_stream_socket
929 || m_actual_state
== m_sm
.m_bound_unknown_socket
)
930 return ev
.formatted_print
931 ("%qE expects a listening stream socket file descriptor"
932 " whereas %qE is bound but not yet listening",
933 m_callee_fndecl
, m_arg
);
934 if (m_actual_state
== m_sm
.m_connected_stream_socket
)
935 return ev
.formatted_print
936 ("%qE expects a listening stream socket file descriptor"
937 " but %qE is connected",
938 m_callee_fndecl
, m_arg
);
941 case EXPECTED_PHASE_CAN_CONNECT
:
943 if (m_actual_state
== m_sm
.m_bound_datagram_socket
944 || m_actual_state
== m_sm
.m_bound_stream_socket
945 || m_actual_state
== m_sm
.m_bound_unknown_socket
)
946 return ev
.formatted_print
947 ("%qE expects a new socket file descriptor but %qE is bound",
948 m_callee_fndecl
, m_arg
);
950 return ev
.formatted_print
951 ("%qE expects a new socket file descriptor", m_callee_fndecl
);
959 state_machine::state_t m_actual_state
;
960 enum expected_phase m_expected_phase
;
963 /* Enum for use by -Wanalyzer-fd-type-mismatch. */
967 EXPECTED_TYPE_SOCKET
,
968 EXPECTED_TYPE_STREAM_SOCKET
971 /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-type-mismatch. */
973 class fd_type_mismatch
: public fd_param_diagnostic
976 fd_type_mismatch (const fd_state_machine
&sm
, tree arg
,
977 const tree callee_fndecl
,
978 state_machine::state_t actual_state
,
979 enum expected_type expected_type
)
980 : fd_param_diagnostic (sm
, arg
, callee_fndecl
),
981 m_actual_state (actual_state
),
982 m_expected_type (expected_type
)
987 get_kind () const final override
989 return "fd_type_mismatch";
993 subclass_equal_p (const pending_diagnostic
&base_other
) const final override
995 const fd_type_mismatch
&sub_other
= (const fd_type_mismatch
&)base_other
;
996 if (!fd_param_diagnostic ::subclass_equal_p (sub_other
))
998 return (m_actual_state
== sub_other
.m_actual_state
999 && m_expected_type
== sub_other
.m_expected_type
);
1003 get_controlling_option () const final override
1005 return OPT_Wanalyzer_fd_type_mismatch
;
1009 emit (diagnostic_emission_context
&ctxt
) final override
1011 switch (m_expected_type
)
1015 case EXPECTED_TYPE_SOCKET
:
1016 return ctxt
.warn ("%qE on non-socket file descriptor %qE",
1017 m_callee_fndecl
, m_arg
);
1018 case EXPECTED_TYPE_STREAM_SOCKET
:
1019 if (m_sm
.is_datagram_socket_fd_p (m_actual_state
))
1020 return ctxt
.warn ("%qE on datagram socket file descriptor %qE",
1021 m_callee_fndecl
, m_arg
);
1023 return ctxt
.warn ("%qE on non-stream-socket file descriptor %qE",
1024 m_callee_fndecl
, m_arg
);
1029 describe_final_event (const evdesc::final_event
&ev
) final override
1031 switch (m_expected_type
)
1036 case EXPECTED_TYPE_SOCKET
:
1037 case EXPECTED_TYPE_STREAM_SOCKET
:
1038 if (!m_sm
.is_socket_fd_p (m_actual_state
))
1039 return ev
.formatted_print ("%qE expects a socket file descriptor"
1040 " but %qE is not a socket",
1041 m_callee_fndecl
, m_arg
);
1043 gcc_assert (m_expected_type
== EXPECTED_TYPE_STREAM_SOCKET
);
1044 gcc_assert (m_sm
.is_datagram_socket_fd_p (m_actual_state
));
1045 return ev
.formatted_print
1046 ("%qE expects a stream socket file descriptor"
1047 " but %qE is a datagram socket",
1048 m_callee_fndecl
, m_arg
);
1052 state_machine::state_t m_actual_state
;
1053 enum expected_type m_expected_type
;
1056 fd_state_machine::fd_state_machine (logger
*logger
)
1057 : state_machine ("file-descriptor", logger
),
1058 m_constant_fd (add_state ("fd-constant")),
1059 m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
1060 m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
1061 m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
1062 m_valid_read_write (add_state ("fd-valid-read-write")),
1063 m_valid_read_only (add_state ("fd-valid-read-only")),
1064 m_valid_write_only (add_state ("fd-valid-write-only")),
1065 m_invalid (add_state ("fd-invalid")),
1066 m_closed (add_state ("fd-closed")),
1067 m_new_datagram_socket (add_state ("fd-new-datagram-socket")),
1068 m_new_stream_socket (add_state ("fd-new-stream-socket")),
1069 m_new_unknown_socket (add_state ("fd-new-unknown-socket")),
1070 m_bound_datagram_socket (add_state ("fd-bound-datagram-socket")),
1071 m_bound_stream_socket (add_state ("fd-bound-stream-socket")),
1072 m_bound_unknown_socket (add_state ("fd-bound-unknown-socket")),
1073 m_listening_stream_socket (add_state ("fd-listening-stream-socket")),
1074 m_connected_stream_socket (add_state ("fd-connected-stream-socket")),
1075 m_stop (add_state ("fd-stop")),
1076 m_O_ACCMODE (get_stashed_constant_by_name ("O_ACCMODE")),
1077 m_O_RDONLY (get_stashed_constant_by_name ("O_RDONLY")),
1078 m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY")),
1079 m_SOCK_STREAM (get_stashed_constant_by_name ("SOCK_STREAM")),
1080 m_SOCK_DGRAM (get_stashed_constant_by_name ("SOCK_DGRAM"))
1085 fd_state_machine::is_unchecked_fd_p (state_t s
) const
1087 return (s
== m_unchecked_read_write
1088 || s
== m_unchecked_read_only
1089 || s
== m_unchecked_write_only
);
1093 fd_state_machine::is_valid_fd_p (state_t s
) const
1095 return (s
== m_valid_read_write
1096 || s
== m_valid_read_only
1097 || s
== m_valid_write_only
);
1101 fd_state_machine::is_socket_fd_p (state_t s
) const
1103 return (s
== m_new_datagram_socket
1104 || s
== m_new_stream_socket
1105 || s
== m_new_unknown_socket
1106 || s
== m_bound_datagram_socket
1107 || s
== m_bound_stream_socket
1108 || s
== m_bound_unknown_socket
1109 || s
== m_listening_stream_socket
1110 || s
== m_connected_stream_socket
);
1114 fd_state_machine::is_datagram_socket_fd_p (state_t s
) const
1116 return (s
== m_new_datagram_socket
1117 || s
== m_new_unknown_socket
1118 || s
== m_bound_datagram_socket
1119 || s
== m_bound_unknown_socket
);
1123 fd_state_machine::is_stream_socket_fd_p (state_t s
) const
1125 return (s
== m_new_stream_socket
1126 || s
== m_new_unknown_socket
1127 || s
== m_bound_stream_socket
1128 || s
== m_bound_unknown_socket
1129 || s
== m_listening_stream_socket
1130 || s
== m_connected_stream_socket
);
1134 fd_state_machine::get_access_mode_from_flag (int flag
) const
1136 if (m_O_ACCMODE
&& TREE_CODE (m_O_ACCMODE
) == INTEGER_CST
)
1138 const unsigned HOST_WIDE_INT mask_val
= TREE_INT_CST_LOW (m_O_ACCMODE
);
1139 const unsigned HOST_WIDE_INT masked_flag
= flag
& mask_val
;
1141 if (m_O_RDONLY
&& TREE_CODE (m_O_RDONLY
) == INTEGER_CST
)
1142 if (masked_flag
== TREE_INT_CST_LOW (m_O_RDONLY
))
1145 if (m_O_WRONLY
&& TREE_CODE (m_O_WRONLY
) == INTEGER_CST
)
1146 if (masked_flag
== TREE_INT_CST_LOW (m_O_WRONLY
))
1153 fd_state_machine::is_readonly_fd_p (state_t state
) const
1155 return (state
== m_unchecked_read_only
|| state
== m_valid_read_only
);
1159 fd_state_machine::is_writeonly_fd_p (state_t state
) const
1161 return (state
== m_unchecked_write_only
|| state
== m_valid_write_only
);
1165 fd_state_machine::is_closed_fd_p (state_t state
) const
1167 return (state
== m_closed
);
1171 fd_state_machine::is_constant_fd_p (state_t state
) const
1173 return (state
== m_constant_fd
);
1176 fd_state_machine::state_t
1177 fd_state_machine::valid_to_unchecked_state (state_t state
) const
1179 if (state
== m_valid_read_write
)
1180 return m_unchecked_read_write
;
1181 else if (state
== m_valid_write_only
)
1182 return m_unchecked_write_only
;
1183 else if (state
== m_valid_read_only
)
1184 return m_unchecked_read_only
;
1191 fd_state_machine::mark_as_valid_fd (region_model
*model
,
1193 const svalue
*fd_sval
,
1194 const extrinsic_state
&ext_state
) const
1196 smap
->set_state (model
, fd_sval
, m_valid_read_write
, NULL
, ext_state
);
1200 fd_state_machine::on_stmt (sm_context
&sm_ctxt
, const supernode
*node
,
1201 const gimple
*stmt
) const
1203 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1204 if (tree callee_fndecl
= sm_ctxt
.get_fndecl_for_call (call
))
1206 if (is_named_call_p (callee_fndecl
, "open", call
, 2))
1208 on_open (sm_ctxt
, node
, stmt
, call
);
1212 if (is_named_call_p (callee_fndecl
, "creat", call
, 2))
1214 on_creat (sm_ctxt
, node
, stmt
, call
);
1218 if (is_named_call_p (callee_fndecl
, "close", call
, 1))
1220 on_close (sm_ctxt
, node
, stmt
, call
);
1224 if (is_named_call_p (callee_fndecl
, "write", call
, 3))
1226 on_write (sm_ctxt
, node
, stmt
, call
, callee_fndecl
);
1230 if (is_named_call_p (callee_fndecl
, "read", call
, 3))
1232 on_read (sm_ctxt
, node
, stmt
, call
, callee_fndecl
);
1236 if (is_named_call_p (callee_fndecl
, "dup", call
, 1))
1238 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_1
);
1242 if (is_named_call_p (callee_fndecl
, "dup2", call
, 2))
1244 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_2
);
1248 if (is_named_call_p (callee_fndecl
, "dup3", call
, 3))
1250 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_3
);
1255 // Handle __attribute__((fd_arg))
1257 check_for_fd_attrs (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1258 "fd_arg", DIRS_READ_WRITE
);
1260 // Handle __attribute__((fd_arg_read))
1262 check_for_fd_attrs (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1263 "fd_arg_read", DIRS_READ
);
1265 // Handle __attribute__((fd_arg_write))
1267 check_for_fd_attrs (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1268 "fd_arg_write", DIRS_WRITE
);
1276 fd_state_machine::check_for_fd_attrs (
1277 sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
1278 const gcall
*call
, const tree callee_fndecl
, const char *attr_name
,
1279 access_directions fd_attr_access_dir
) const
1281 /* Handle interesting fd attributes of the callee_fndecl,
1282 or prioritize those of the builtin that callee_fndecl is
1284 Might want this to be controlled by a flag. */
1285 tree fndecl
= callee_fndecl
;
1286 /* If call is recognized as a builtin known_function,
1287 use that builtin's function_decl. */
1288 if (const region_model
*old_model
= sm_ctxt
.get_old_region_model ())
1289 if (const builtin_known_function
*builtin_kf
1290 = old_model
->get_builtin_kf (call
))
1291 fndecl
= builtin_kf
->builtin_decl ();
1293 tree attrs
= TYPE_ATTRIBUTES (TREE_TYPE (fndecl
));
1294 attrs
= lookup_attribute (attr_name
, attrs
);
1298 if (!TREE_VALUE (attrs
))
1303 for (tree idx
= TREE_VALUE (attrs
); idx
; idx
= TREE_CHAIN (idx
))
1305 unsigned int val
= TREE_INT_CST_LOW (TREE_VALUE (idx
)) - 1;
1306 bitmap_set_bit (argmap
, val
);
1308 if (bitmap_empty_p (argmap
))
1311 for (unsigned arg_idx
= 0; arg_idx
< gimple_call_num_args (call
); arg_idx
++)
1313 tree arg
= gimple_call_arg (call
, arg_idx
);
1314 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (arg
);
1315 state_t state
= sm_ctxt
.get_state (stmt
, arg
);
1316 bool bit_set
= bitmap_bit_p (argmap
, arg_idx
);
1317 if (TREE_CODE (TREE_TYPE (arg
)) != INTEGER_TYPE
)
1319 if (bit_set
) // Check if arg_idx is marked by any of the file descriptor
1323 /* Do use the fndecl that caused the warning so that the
1324 misused attributes are printed and the user not confused. */
1325 if (is_closed_fd_p (state
))
1328 sm_ctxt
.warn (node
, stmt
, arg
,
1329 make_unique
<fd_use_after_close
>
1336 if (!(is_valid_fd_p (state
) || (state
== m_stop
)))
1338 if (!is_constant_fd_p (state
))
1340 sm_ctxt
.warn (node
, stmt
, arg
,
1341 make_unique
<fd_use_without_check
>
1349 switch (fd_attr_access_dir
)
1351 case DIRS_READ_WRITE
:
1355 if (is_writeonly_fd_p (state
))
1359 make_unique
<fd_access_mode_mismatch
> (*this, diag_arg
,
1369 if (is_readonly_fd_p (state
))
1373 make_unique
<fd_access_mode_mismatch
> (*this, diag_arg
,
1388 fd_state_machine::on_open (sm_context
&sm_ctxt
, const supernode
*node
,
1389 const gimple
*stmt
, const gcall
*call
) const
1391 tree lhs
= gimple_call_lhs (call
);
1394 tree arg
= gimple_call_arg (call
, 1);
1395 enum access_mode mode
= READ_WRITE
;
1396 if (TREE_CODE (arg
) == INTEGER_CST
)
1398 int flag
= TREE_INT_CST_LOW (arg
);
1399 mode
= get_access_mode_from_flag (flag
);
1404 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_start
,
1405 m_unchecked_read_only
);
1408 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_start
,
1409 m_unchecked_write_only
);
1412 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_start
,
1413 m_unchecked_read_write
);
1418 sm_ctxt
.warn (node
, stmt
, NULL_TREE
,
1419 make_unique
<fd_leak
> (*this, NULL_TREE
));
1424 fd_state_machine::on_creat (sm_context
&sm_ctxt
, const supernode
*node
,
1425 const gimple
*stmt
, const gcall
*call
) const
1427 tree lhs
= gimple_call_lhs (call
);
1429 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_start
, m_unchecked_write_only
);
1431 sm_ctxt
.warn (node
, stmt
, NULL_TREE
,
1432 make_unique
<fd_leak
> (*this, NULL_TREE
));
1436 fd_state_machine::check_for_dup (sm_context
&sm_ctxt
, const supernode
*node
,
1437 const gimple
*stmt
, const gcall
*call
,
1438 const tree callee_fndecl
, enum dup kind
) const
1440 tree lhs
= gimple_call_lhs (call
);
1441 tree arg_1
= gimple_call_arg (call
, 0);
1442 state_t state_arg_1
= sm_ctxt
.get_state (stmt
, arg_1
);
1443 if (state_arg_1
== m_stop
)
1445 if (!(is_constant_fd_p (state_arg_1
) || is_valid_fd_p (state_arg_1
)
1446 || state_arg_1
== m_start
))
1448 check_for_open_fd (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1457 if (is_constant_fd_p (state_arg_1
) || state_arg_1
== m_start
)
1458 sm_ctxt
.set_next_state (stmt
, lhs
, m_unchecked_read_write
);
1460 sm_ctxt
.set_next_state (stmt
, lhs
,
1461 valid_to_unchecked_state (state_arg_1
));
1467 tree arg_2
= gimple_call_arg (call
, 1);
1468 state_t state_arg_2
= sm_ctxt
.get_state (stmt
, arg_2
);
1469 tree diag_arg_2
= sm_ctxt
.get_diagnostic_tree (arg_2
);
1470 if (state_arg_2
== m_stop
)
1472 /* Check if -1 was passed as second argument to dup2. */
1473 if (!(is_constant_fd_p (state_arg_2
) || is_valid_fd_p (state_arg_2
)
1474 || state_arg_2
== m_start
))
1478 make_unique
<fd_use_without_check
> (*this, diag_arg_2
,
1482 /* dup2 returns value of its second argument on success.But, the
1483 access mode of the returned file descriptor depends on the duplicated
1484 file descriptor i.e the first argument. */
1487 if (is_constant_fd_p (state_arg_1
) || state_arg_1
== m_start
)
1488 sm_ctxt
.set_next_state (stmt
, lhs
, m_unchecked_read_write
);
1490 sm_ctxt
.set_next_state (stmt
, lhs
,
1491 valid_to_unchecked_state (state_arg_1
));
1499 fd_state_machine::on_close (sm_context
&sm_ctxt
, const supernode
*node
,
1500 const gimple
*stmt
, const gcall
*call
) const
1502 tree arg
= gimple_call_arg (call
, 0);
1503 state_t state
= sm_ctxt
.get_state (stmt
, arg
);
1504 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (arg
);
1506 sm_ctxt
.on_transition (node
, stmt
, arg
, m_start
, m_closed
);
1507 sm_ctxt
.on_transition (node
, stmt
, arg
, m_unchecked_read_write
, m_closed
);
1508 sm_ctxt
.on_transition (node
, stmt
, arg
, m_unchecked_read_only
, m_closed
);
1509 sm_ctxt
.on_transition (node
, stmt
, arg
, m_unchecked_write_only
, m_closed
);
1510 sm_ctxt
.on_transition (node
, stmt
, arg
, m_valid_read_write
, m_closed
);
1511 sm_ctxt
.on_transition (node
, stmt
, arg
, m_valid_read_only
, m_closed
);
1512 sm_ctxt
.on_transition (node
, stmt
, arg
, m_valid_write_only
, m_closed
);
1513 sm_ctxt
.on_transition (node
, stmt
, arg
, m_constant_fd
, m_closed
);
1514 sm_ctxt
.on_transition (node
, stmt
, arg
, m_new_datagram_socket
, m_closed
);
1515 sm_ctxt
.on_transition (node
, stmt
, arg
, m_new_stream_socket
, m_closed
);
1516 sm_ctxt
.on_transition (node
, stmt
, arg
, m_new_unknown_socket
, m_closed
);
1517 sm_ctxt
.on_transition (node
, stmt
, arg
, m_bound_datagram_socket
, m_closed
);
1518 sm_ctxt
.on_transition (node
, stmt
, arg
, m_bound_stream_socket
, m_closed
);
1519 sm_ctxt
.on_transition (node
, stmt
, arg
, m_bound_unknown_socket
, m_closed
);
1520 sm_ctxt
.on_transition (node
, stmt
, arg
, m_listening_stream_socket
, m_closed
);
1521 sm_ctxt
.on_transition (node
, stmt
, arg
, m_connected_stream_socket
, m_closed
);
1523 if (is_closed_fd_p (state
))
1525 sm_ctxt
.warn (node
, stmt
, arg
,
1526 make_unique
<fd_double_close
> (*this, diag_arg
));
1527 sm_ctxt
.set_next_state (stmt
, arg
, m_stop
);
1531 fd_state_machine::on_read (sm_context
&sm_ctxt
, const supernode
*node
,
1532 const gimple
*stmt
, const gcall
*call
,
1533 const tree callee_fndecl
) const
1535 check_for_open_fd (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DIRS_READ
);
1538 fd_state_machine::on_write (sm_context
&sm_ctxt
, const supernode
*node
,
1539 const gimple
*stmt
, const gcall
*call
,
1540 const tree callee_fndecl
) const
1542 check_for_open_fd (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DIRS_WRITE
);
1546 fd_state_machine::check_for_open_fd (
1547 sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
1548 const gcall
*call
, const tree callee_fndecl
,
1549 enum access_directions callee_fndecl_dir
) const
1551 tree arg
= gimple_call_arg (call
, 0);
1552 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (arg
);
1553 state_t state
= sm_ctxt
.get_state (stmt
, arg
);
1555 if (is_closed_fd_p (state
))
1557 sm_ctxt
.warn (node
, stmt
, arg
,
1558 make_unique
<fd_use_after_close
> (*this, diag_arg
,
1564 if (state
== m_new_stream_socket
1565 || state
== m_bound_stream_socket
1566 || state
== m_listening_stream_socket
)
1567 /* Complain about fncall on socket in wrong phase. */
1570 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
1573 EXPECTED_PHASE_CAN_TRANSFER
));
1574 else if (!(is_valid_fd_p (state
)
1575 || state
== m_new_datagram_socket
1576 || state
== m_bound_unknown_socket
1577 || state
== m_connected_stream_socket
1579 || state
== m_stop
))
1581 if (!is_constant_fd_p (state
))
1584 make_unique
<fd_use_without_check
> (*this, diag_arg
,
1587 switch (callee_fndecl_dir
)
1589 case DIRS_READ_WRITE
:
1592 if (is_writeonly_fd_p (state
))
1594 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (arg
);
1595 sm_ctxt
.warn (node
, stmt
, arg
,
1596 make_unique
<fd_access_mode_mismatch
> (
1597 *this, diag_arg
, DIRS_WRITE
, callee_fndecl
));
1603 if (is_readonly_fd_p (state
))
1605 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (arg
);
1606 sm_ctxt
.warn (node
, stmt
, arg
,
1607 make_unique
<fd_access_mode_mismatch
> (
1608 *this, diag_arg
, DIRS_READ
, callee_fndecl
));
1616 add_constraint_ge_zero (region_model
*model
,
1617 const svalue
*fd_sval
,
1618 region_model_context
*ctxt
)
1621 = model
->get_manager ()->get_or_create_int_cst (integer_type_node
, 0);
1622 return model
->add_constraint (fd_sval
, GE_EXPR
, zero
, ctxt
);
1625 /* Get the state for a new socket type based on SOCKET_TYPE_SVAL,
1628 state_machine::state_t
1630 get_state_for_socket_type (const svalue
*socket_type_sval
) const
1632 if (tree socket_type_cst
= socket_type_sval
->maybe_get_constant ())
1634 /* Attempt to use SOCK_* constants stashed from the frontend. */
1635 if (tree_int_cst_equal (socket_type_cst
, m_SOCK_STREAM
))
1636 return m_new_stream_socket
;
1637 if (tree_int_cst_equal (socket_type_cst
, m_SOCK_DGRAM
))
1638 return m_new_datagram_socket
;
1641 /* Unrecognized constant, or a symbolic "type" value. */
1642 return m_new_unknown_socket
;
1645 /* Update the model and fd state for an outcome of a call to "socket",
1646 where SUCCESSFUL indicate which of the two outcomes.
1647 Return true if the outcome is feasible, or false to reject it. */
1650 fd_state_machine::on_socket (const call_details
&cd
,
1652 sm_context
&sm_ctxt
,
1653 const extrinsic_state
&ext_state
) const
1655 const gcall
*stmt
= cd
.get_call_stmt ();
1656 engine
*eng
= ext_state
.get_engine ();
1657 const supergraph
*sg
= eng
->get_supergraph ();
1658 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
1659 region_model
*model
= cd
.get_model ();
1663 if (gimple_call_lhs (stmt
))
1665 conjured_purge
p (model
, cd
.get_ctxt ());
1666 region_model_manager
*mgr
= model
->get_manager ();
1667 const svalue
*new_fd
1668 = mgr
->get_or_create_conjured_svalue (integer_type_node
,
1670 cd
.get_lhs_region (),
1672 if (!add_constraint_ge_zero (model
, new_fd
, cd
.get_ctxt ()))
1675 const svalue
*socket_type_sval
= cd
.get_arg_svalue (1);
1676 state_machine::state_t new_state
1677 = get_state_for_socket_type (socket_type_sval
);
1678 sm_ctxt
.on_transition (node
, stmt
, new_fd
, m_start
, new_state
);
1679 model
->set_value (cd
.get_lhs_region (), new_fd
, cd
.get_ctxt ());
1682 sm_ctxt
.warn (node
, stmt
, NULL_TREE
,
1683 make_unique
<fd_leak
> (*this, NULL_TREE
));
1687 /* Return -1; set errno. */
1688 model
->update_for_int_cst_return (cd
, -1, true);
1689 model
->set_errno (cd
);
1695 /* Check that FD_SVAL is usable by socket APIs.
1696 Complain if it has been closed, if it is a non-socket,
1698 If COMPLAINED is non-NULL and a problem is found,
1699 write *COMPLAINED = true.
1701 If SUCCESSFUL is true, attempt to add the constraint that FD_SVAL >= 0.
1702 Return true if this outcome is feasible. */
1705 fd_state_machine::check_for_socket_fd (const call_details
&cd
,
1707 sm_context
&sm_ctxt
,
1708 const svalue
*fd_sval
,
1709 const supernode
*node
,
1711 bool *complained
) const
1713 const gcall
*stmt
= cd
.get_call_stmt ();
1715 if (is_closed_fd_p (old_state
))
1717 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (fd_sval
);
1719 (node
, stmt
, fd_sval
,
1720 make_unique
<fd_use_after_close
> (*this, diag_arg
,
1721 cd
.get_fndecl_for_call ()));
1727 else if (is_unchecked_fd_p (old_state
) || is_valid_fd_p (old_state
))
1729 /* Complain about non-socket. */
1730 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (fd_sval
);
1732 (node
, stmt
, fd_sval
,
1733 make_unique
<fd_type_mismatch
> (*this, diag_arg
,
1734 cd
.get_fndecl_for_call (),
1736 EXPECTED_TYPE_SOCKET
));
1742 else if (old_state
== m_invalid
)
1744 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (fd_sval
);
1746 (node
, stmt
, fd_sval
,
1747 make_unique
<fd_use_without_check
> (*this, diag_arg
,
1748 cd
.get_fndecl_for_call ()));
1756 if (!add_constraint_ge_zero (cd
.get_model (), fd_sval
, cd
.get_ctxt ()))
1762 /* For use by "bind" and "connect".
1763 As per fd_state_machine::check_for_socket_fd above,
1764 but also complain if we don't have a new socket, and check that
1765 we can read up to the size bytes from the address. */
1768 fd_state_machine::check_for_new_socket_fd (const call_details
&cd
,
1770 sm_context
&sm_ctxt
,
1771 const svalue
*fd_sval
,
1772 const supernode
*node
,
1774 enum expected_phase expected_phase
)
1777 bool complained
= false;
1779 /* Check address and len. */
1780 const svalue
*address_sval
= cd
.get_arg_svalue (1);
1781 const svalue
*len_sval
= cd
.get_arg_svalue (2);
1783 /* Check that we can read the given number of bytes from the
1785 region_model
*model
= cd
.get_model ();
1786 const region
*address_reg
1787 = model
->deref_rvalue (address_sval
, cd
.get_arg_tree (1),
1789 const region
*sized_address_reg
1790 = model
->get_manager ()->get_sized_region (address_reg
,
1793 model
->get_store_value (sized_address_reg
, cd
.get_ctxt ());
1795 if (!check_for_socket_fd (cd
, successful
, sm_ctxt
,
1796 fd_sval
, node
, old_state
, &complained
))
1798 else if (!complained
1799 && !(old_state
== m_new_stream_socket
1800 || old_state
== m_new_datagram_socket
1801 || old_state
== m_new_unknown_socket
1802 || old_state
== m_start
1803 || old_state
== m_stop
1804 || old_state
== m_constant_fd
))
1806 /* Complain about "bind" or "connect" in wrong phase. */
1807 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (fd_sval
);
1809 (node
, cd
.get_call_stmt (), fd_sval
,
1810 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
1811 cd
.get_fndecl_for_call (),
1817 else if (!successful
)
1819 /* If we were in the start state, assume we had a new socket. */
1820 if (old_state
== m_start
)
1821 sm_ctxt
.set_next_state (cd
.get_call_stmt (), fd_sval
,
1822 m_new_unknown_socket
);
1825 /* Passing NULL as the address will lead to failure. */
1827 if (address_sval
->all_zeroes_p ())
1833 /* Update the model and fd state for an outcome of a call to "bind",
1834 where SUCCESSFUL indicate which of the two outcomes.
1835 Return true if the outcome is feasible, or false to reject it. */
1838 fd_state_machine::on_bind (const call_details
&cd
,
1840 sm_context
&sm_ctxt
,
1841 const extrinsic_state
&ext_state
) const
1843 const gcall
*stmt
= cd
.get_call_stmt ();
1844 engine
*eng
= ext_state
.get_engine ();
1845 const supergraph
*sg
= eng
->get_supergraph ();
1846 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
1847 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
1848 region_model
*model
= cd
.get_model ();
1849 state_t old_state
= sm_ctxt
.get_state (stmt
, fd_sval
);
1851 if (!check_for_new_socket_fd (cd
, successful
, sm_ctxt
,
1852 fd_sval
, node
, old_state
,
1853 EXPECTED_PHASE_CAN_BIND
))
1858 state_t next_state
= NULL
;
1859 if (old_state
== m_new_stream_socket
)
1860 next_state
= m_bound_stream_socket
;
1861 else if (old_state
== m_new_datagram_socket
)
1862 next_state
= m_bound_datagram_socket
;
1863 else if (old_state
== m_new_unknown_socket
)
1864 next_state
= m_bound_unknown_socket
;
1865 else if (old_state
== m_start
1866 || old_state
== m_constant_fd
)
1867 next_state
= m_bound_unknown_socket
;
1868 else if (old_state
== m_stop
)
1869 next_state
= m_stop
;
1872 sm_ctxt
.set_next_state (cd
.get_call_stmt (), fd_sval
, next_state
);
1873 model
->update_for_zero_return (cd
, true);
1877 /* Return -1; set errno. */
1878 model
->update_for_int_cst_return (cd
, -1, true);
1879 model
->set_errno (cd
);
1885 /* Update the model and fd state for an outcome of a call to "listen",
1886 where SUCCESSFUL indicate which of the two outcomes.
1887 Return true if the outcome is feasible, or false to reject it. */
1890 fd_state_machine::on_listen (const call_details
&cd
,
1892 sm_context
&sm_ctxt
,
1893 const extrinsic_state
&ext_state
) const
1895 const gcall
*stmt
= cd
.get_call_stmt ();
1896 engine
*eng
= ext_state
.get_engine ();
1897 const supergraph
*sg
= eng
->get_supergraph ();
1898 const supernode
*node
= sg
->get_supernode_for_stmt (cd
.get_call_stmt ());
1899 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
1900 region_model
*model
= cd
.get_model ();
1901 state_t old_state
= sm_ctxt
.get_state (stmt
, fd_sval
);
1903 /* We expect a stream socket that's had "bind" called on it. */
1904 if (!check_for_socket_fd (cd
, successful
, sm_ctxt
, fd_sval
, node
, old_state
))
1906 if (!(old_state
== m_start
1907 || old_state
== m_constant_fd
1908 || old_state
== m_stop
1909 || old_state
== m_invalid
1910 || old_state
== m_bound_stream_socket
1911 || old_state
== m_bound_unknown_socket
1912 /* Assume it's OK to call "listen" more than once. */
1913 || old_state
== m_listening_stream_socket
))
1915 /* Complain about fncall on wrong type or in wrong phase. */
1916 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (fd_sval
);
1917 if (is_stream_socket_fd_p (old_state
))
1919 (node
, stmt
, fd_sval
,
1920 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
1921 cd
.get_fndecl_for_call (),
1923 EXPECTED_PHASE_CAN_LISTEN
));
1926 (node
, stmt
, fd_sval
,
1927 make_unique
<fd_type_mismatch
> (*this, diag_arg
,
1928 cd
.get_fndecl_for_call (),
1930 EXPECTED_TYPE_STREAM_SOCKET
));
1937 model
->update_for_zero_return (cd
, true);
1938 sm_ctxt
.set_next_state (cd
.get_call_stmt (), fd_sval
,
1939 m_listening_stream_socket
);
1943 /* Return -1; set errno. */
1944 model
->update_for_int_cst_return (cd
, -1, true);
1945 model
->set_errno (cd
);
1946 if (old_state
== m_start
)
1947 sm_ctxt
.set_next_state (cd
.get_call_stmt (), fd_sval
,
1948 m_bound_stream_socket
);
1954 /* Update the model and fd state for an outcome of a call to "accept",
1955 where SUCCESSFUL indicate which of the two outcomes.
1956 Return true if the outcome is feasible, or false to reject it. */
1959 fd_state_machine::on_accept (const call_details
&cd
,
1961 sm_context
&sm_ctxt
,
1962 const extrinsic_state
&ext_state
) const
1964 const gcall
*stmt
= cd
.get_call_stmt ();
1965 engine
*eng
= ext_state
.get_engine ();
1966 const supergraph
*sg
= eng
->get_supergraph ();
1967 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
1968 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
1969 const svalue
*address_sval
= cd
.get_arg_svalue (1);
1970 const svalue
*len_ptr_sval
= cd
.get_arg_svalue (2);
1971 region_model
*model
= cd
.get_model ();
1972 state_t old_state
= sm_ctxt
.get_state (stmt
, fd_sval
);
1974 if (!address_sval
->all_zeroes_p ())
1976 region_model_manager
*mgr
= model
->get_manager ();
1978 /* We might have a union of various pointer types, rather than a
1979 pointer type; cast to (void *) before dereferencing. */
1980 address_sval
= mgr
->get_or_create_cast (ptr_type_node
, address_sval
);
1982 const region
*address_reg
1983 = model
->deref_rvalue (address_sval
, cd
.get_arg_tree (1),
1985 const region
*len_reg
1986 = model
->deref_rvalue (len_ptr_sval
, cd
.get_arg_tree (2),
1988 const svalue
*old_len_sval
1989 = model
->get_store_value (len_reg
, cd
.get_ctxt ());
1990 tree len_ptr
= cd
.get_arg_tree (2);
1991 tree star_len_ptr
= build2 (MEM_REF
, TREE_TYPE (TREE_TYPE (len_ptr
)),
1993 build_int_cst (TREE_TYPE (len_ptr
), 0));
1994 old_len_sval
= model
->check_for_poison (old_len_sval
,
2000 conjured_purge
p (model
, cd
.get_ctxt ());
2001 const region
*old_sized_address_reg
2002 = mgr
->get_sized_region (address_reg
,
2005 const svalue
*new_addr_sval
2006 = mgr
->get_or_create_conjured_svalue (NULL_TREE
,
2008 old_sized_address_reg
,
2010 model
->set_value (old_sized_address_reg
, new_addr_sval
,
2012 const svalue
*new_addr_len
2013 = mgr
->get_or_create_conjured_svalue (NULL_TREE
,
2017 model
->set_value (len_reg
, new_addr_len
, cd
.get_ctxt ());
2021 /* We expect a stream socket in the "listening" state. */
2022 if (!check_for_socket_fd (cd
, successful
, sm_ctxt
, fd_sval
, node
, old_state
))
2025 if (old_state
== m_start
|| old_state
== m_constant_fd
)
2026 /* If we were in the start state (or a constant), assume we had the
2028 sm_ctxt
.set_next_state (cd
.get_call_stmt (), fd_sval
,
2029 m_listening_stream_socket
);
2030 else if (old_state
== m_stop
)
2032 /* No further complaints. */
2034 else if (old_state
!= m_listening_stream_socket
)
2036 /* Complain about fncall on wrong type or in wrong phase. */
2037 tree diag_arg
= sm_ctxt
.get_diagnostic_tree (fd_sval
);
2038 if (is_stream_socket_fd_p (old_state
))
2040 (node
, stmt
, fd_sval
,
2041 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
2042 cd
.get_fndecl_for_call (),
2044 EXPECTED_PHASE_CAN_ACCEPT
));
2047 (node
, stmt
, fd_sval
,
2048 make_unique
<fd_type_mismatch
> (*this, diag_arg
,
2049 cd
.get_fndecl_for_call (),
2051 EXPECTED_TYPE_STREAM_SOCKET
));
2058 /* Return new conjured FD in "connected" state. */
2059 if (gimple_call_lhs (stmt
))
2061 conjured_purge
p (model
, cd
.get_ctxt ());
2062 region_model_manager
*mgr
= model
->get_manager ();
2063 const svalue
*new_fd
2064 = mgr
->get_or_create_conjured_svalue (integer_type_node
,
2066 cd
.get_lhs_region (),
2068 if (!add_constraint_ge_zero (model
, new_fd
, cd
.get_ctxt ()))
2070 sm_ctxt
.on_transition (node
, stmt
, new_fd
,
2071 m_start
, m_connected_stream_socket
);
2072 model
->set_value (cd
.get_lhs_region (), new_fd
, cd
.get_ctxt ());
2075 sm_ctxt
.warn (node
, stmt
, NULL_TREE
,
2076 make_unique
<fd_leak
> (*this, NULL_TREE
));
2080 /* Return -1; set errno. */
2081 model
->update_for_int_cst_return (cd
, -1, true);
2082 model
->set_errno (cd
);
2088 /* Update the model and fd state for an outcome of a call to "connect",
2089 where SUCCESSFUL indicate which of the two outcomes.
2090 Return true if the outcome is feasible, or false to reject it. */
2093 fd_state_machine::on_connect (const call_details
&cd
,
2095 sm_context
&sm_ctxt
,
2096 const extrinsic_state
&ext_state
) const
2098 const gcall
*stmt
= cd
.get_call_stmt ();
2099 engine
*eng
= ext_state
.get_engine ();
2100 const supergraph
*sg
= eng
->get_supergraph ();
2101 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
2102 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
2103 region_model
*model
= cd
.get_model ();
2104 state_t old_state
= sm_ctxt
.get_state (stmt
, fd_sval
);
2106 if (!check_for_new_socket_fd (cd
, successful
, sm_ctxt
,
2107 fd_sval
, node
, old_state
,
2108 EXPECTED_PHASE_CAN_CONNECT
))
2113 model
->update_for_zero_return (cd
, true);
2114 state_t next_state
= NULL
;
2115 if (old_state
== m_new_stream_socket
)
2116 next_state
= m_connected_stream_socket
;
2117 else if (old_state
== m_new_datagram_socket
)
2118 /* It's legal to call connect on a datagram socket, potentially
2119 more than once. We don't transition states for this. */
2120 next_state
= m_new_datagram_socket
;
2121 else if (old_state
== m_new_unknown_socket
)
2122 next_state
= m_stop
;
2123 else if (old_state
== m_start
2124 || old_state
== m_constant_fd
)
2125 next_state
= m_stop
;
2126 else if (old_state
== m_stop
)
2127 next_state
= m_stop
;
2130 sm_ctxt
.set_next_state (cd
.get_call_stmt (), fd_sval
, next_state
);
2134 /* Return -1; set errno. */
2135 model
->update_for_int_cst_return (cd
, -1, true);
2136 model
->set_errno (cd
);
2137 /* TODO: perhaps transition to a failed state, since the
2138 portable way to handle a failed "connect" is to close
2139 the socket and try again with a new socket. */
2146 fd_state_machine::on_condition (sm_context
&sm_ctxt
, const supernode
*node
,
2147 const gimple
*stmt
, const svalue
*lhs
,
2148 enum tree_code op
, const svalue
*rhs
) const
2150 if (tree cst
= rhs
->maybe_get_constant ())
2152 if (TREE_CODE (cst
) == INTEGER_CST
)
2154 int val
= TREE_INT_CST_LOW (cst
);
2158 make_valid_transitions_on_condition (sm_ctxt
, node
, stmt
, lhs
);
2160 else if (op
== EQ_EXPR
)
2161 make_invalid_transitions_on_condition (sm_ctxt
, node
, stmt
,
2167 if (rhs
->all_zeroes_p ())
2170 make_valid_transitions_on_condition (sm_ctxt
, node
, stmt
, lhs
);
2171 else if (op
== LT_EXPR
)
2172 make_invalid_transitions_on_condition (sm_ctxt
, node
, stmt
, lhs
);
2177 fd_state_machine::make_valid_transitions_on_condition (sm_context
&sm_ctxt
,
2178 const supernode
*node
,
2180 const svalue
*lhs
) const
2182 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_unchecked_read_write
,
2183 m_valid_read_write
);
2184 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_unchecked_read_only
,
2186 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_unchecked_write_only
,
2187 m_valid_write_only
);
2191 fd_state_machine::make_invalid_transitions_on_condition (
2192 sm_context
&sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
2193 const svalue
*lhs
) const
2195 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_unchecked_read_write
, m_invalid
);
2196 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_unchecked_read_only
, m_invalid
);
2197 sm_ctxt
.on_transition (node
, stmt
, lhs
, m_unchecked_write_only
, m_invalid
);
2201 fd_state_machine::can_purge_p (state_t s
) const
2203 if (is_unchecked_fd_p (s
)
2204 || is_valid_fd_p (s
)
2205 || is_socket_fd_p (s
))
2211 std::unique_ptr
<pending_diagnostic
>
2212 fd_state_machine::on_leak (tree var
) const
2214 return make_unique
<fd_leak
> (*this, var
);
2219 make_fd_state_machine (logger
*logger
)
2221 return new fd_state_machine (logger
);
2225 get_fd_state (region_model_context
*ctxt
,
2226 sm_state_map
**out_smap
,
2227 const fd_state_machine
**out_sm
,
2228 unsigned *out_sm_idx
,
2229 std::unique_ptr
<sm_context
> *out_sm_context
)
2234 const state_machine
*sm
;
2235 if (!ctxt
->get_fd_map (out_smap
, &sm
, out_sm_idx
, out_sm_context
))
2240 *out_sm
= (const fd_state_machine
*)sm
;
2244 /* Specialcase hook for handling pipe, for use by
2245 kf_pipe::success::update_model. */
2248 region_model::mark_as_valid_fd (const svalue
*sval
, region_model_context
*ctxt
)
2251 const fd_state_machine
*fd_sm
;
2252 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, NULL
))
2254 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2257 fd_sm
->mark_as_valid_fd (this, smap
, sval
, *ext_state
);
2260 /* Handle calls to "socket".
2261 See e.g. https://man7.org/linux/man-pages/man3/socket.3p.html */
2263 class kf_socket
: public known_function
2266 class outcome_of_socket
: public succeed_or_fail_call_info
2269 outcome_of_socket (const call_details
&cd
, bool success
)
2270 : succeed_or_fail_call_info (cd
, success
)
2273 bool update_model (region_model
*model
,
2274 const exploded_edge
*,
2275 region_model_context
*ctxt
) const final override
2277 const call_details
cd (get_call_details (model
, ctxt
));
2279 const fd_state_machine
*fd_sm
;
2280 std::unique_ptr
<sm_context
> sm_ctxt
;
2281 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2283 cd
.set_any_lhs_with_defaults ();
2286 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2289 cd
.set_any_lhs_with_defaults ();
2293 return fd_sm
->on_socket (cd
, m_success
, *(sm_ctxt
.get ()), *ext_state
);
2297 bool matches_call_types_p (const call_details
&cd
) const final override
2299 return cd
.num_args () == 3;
2302 void impl_call_post (const call_details
&cd
) const final override
2306 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_socket
> (cd
, false));
2307 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_socket
> (cd
, true));
2308 cd
.get_ctxt ()->terminate_path ();
2313 /* Handle calls to "bind".
2314 See e.g. https://man7.org/linux/man-pages/man3/bind.3p.html */
2316 class kf_bind
: public known_function
2319 class outcome_of_bind
: public succeed_or_fail_call_info
2322 outcome_of_bind (const call_details
&cd
, bool success
)
2323 : succeed_or_fail_call_info (cd
, success
)
2326 bool update_model (region_model
*model
,
2327 const exploded_edge
*,
2328 region_model_context
*ctxt
) const final override
2330 const call_details
cd (get_call_details (model
, ctxt
));
2332 const fd_state_machine
*fd_sm
;
2333 std::unique_ptr
<sm_context
> sm_ctxt
;
2334 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2336 cd
.set_any_lhs_with_defaults ();
2339 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2342 cd
.set_any_lhs_with_defaults ();
2345 return fd_sm
->on_bind (cd
, m_success
, *sm_ctxt
.get (), *ext_state
);
2349 bool matches_call_types_p (const call_details
&cd
) const final override
2351 return (cd
.num_args () == 3 && cd
.arg_is_pointer_p (1));
2354 void impl_call_post (const call_details
&cd
) const final override
2358 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_bind
> (cd
, false));
2359 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_bind
> (cd
, true));
2360 cd
.get_ctxt ()->terminate_path ();
2365 /* Handle calls to "listen".
2366 See e.g. https://man7.org/linux/man-pages/man3/listen.3p.html */
2368 class kf_listen
: public known_function
2370 class outcome_of_listen
: public succeed_or_fail_call_info
2373 outcome_of_listen (const call_details
&cd
, bool success
)
2374 : succeed_or_fail_call_info (cd
, success
)
2377 bool update_model (region_model
*model
,
2378 const exploded_edge
*,
2379 region_model_context
*ctxt
) const final override
2381 const call_details
cd (get_call_details (model
, ctxt
));
2383 const fd_state_machine
*fd_sm
;
2384 std::unique_ptr
<sm_context
> sm_ctxt
;
2385 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2387 cd
.set_any_lhs_with_defaults ();
2390 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2393 cd
.set_any_lhs_with_defaults ();
2397 return fd_sm
->on_listen (cd
, m_success
, *sm_ctxt
.get (), *ext_state
);
2401 bool matches_call_types_p (const call_details
&cd
) const final override
2403 return cd
.num_args () == 2;
2406 void impl_call_post (const call_details
&cd
) const final override
2410 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_listen
> (cd
, false));
2411 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_listen
> (cd
, true));
2412 cd
.get_ctxt ()->terminate_path ();
2417 /* Handle calls to "accept".
2418 See e.g. https://man7.org/linux/man-pages/man3/accept.3p.html */
2420 class kf_accept
: public known_function
2422 class outcome_of_accept
: public succeed_or_fail_call_info
2425 outcome_of_accept (const call_details
&cd
, bool success
)
2426 : succeed_or_fail_call_info (cd
, success
)
2429 bool update_model (region_model
*model
,
2430 const exploded_edge
*,
2431 region_model_context
*ctxt
) const final override
2433 const call_details
cd (get_call_details (model
, ctxt
));
2435 const fd_state_machine
*fd_sm
;
2436 std::unique_ptr
<sm_context
> sm_ctxt
;
2437 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2439 cd
.set_any_lhs_with_defaults ();
2442 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2445 cd
.set_any_lhs_with_defaults ();
2449 return fd_sm
->on_accept (cd
, m_success
, *sm_ctxt
.get (), *ext_state
);
2453 bool matches_call_types_p (const call_details
&cd
) const final override
2455 return (cd
.num_args () == 3
2456 && cd
.arg_is_pointer_p (1)
2457 && cd
.arg_is_pointer_p (2));
2460 void impl_call_post (const call_details
&cd
) const final override
2464 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_accept
> (cd
, false));
2465 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_accept
> (cd
, true));
2466 cd
.get_ctxt ()->terminate_path ();
2471 /* Handle calls to "connect".
2472 See e.g. https://man7.org/linux/man-pages/man3/connect.3p.html */
2474 class kf_connect
: public known_function
2477 class outcome_of_connect
: public succeed_or_fail_call_info
2480 outcome_of_connect (const call_details
&cd
, bool success
)
2481 : succeed_or_fail_call_info (cd
, success
)
2484 bool update_model (region_model
*model
,
2485 const exploded_edge
*,
2486 region_model_context
*ctxt
) const final override
2488 const call_details
cd (get_call_details (model
, ctxt
));
2490 const fd_state_machine
*fd_sm
;
2491 std::unique_ptr
<sm_context
> sm_ctxt
;
2492 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2494 cd
.set_any_lhs_with_defaults ();
2497 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2500 cd
.set_any_lhs_with_defaults ();
2504 return fd_sm
->on_connect (cd
, m_success
, *sm_ctxt
.get (), *ext_state
);
2508 bool matches_call_types_p (const call_details
&cd
) const final override
2510 return (cd
.num_args () == 3
2511 && cd
.arg_is_pointer_p (1));
2514 void impl_call_post (const call_details
&cd
) const final override
2518 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_connect
> (cd
, false));
2519 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_connect
> (cd
, true));
2520 cd
.get_ctxt ()->terminate_path ();
2525 /* Handler for "isatty"".
2526 See e.g. https://man7.org/linux/man-pages/man3/isatty.3.html */
2528 class kf_isatty
: public known_function
2530 class outcome_of_isatty
: public succeed_or_fail_call_info
2533 outcome_of_isatty (const call_details
&cd
, bool success
)
2534 : succeed_or_fail_call_info (cd
, success
)
2537 bool update_model (region_model
*model
,
2538 const exploded_edge
*,
2539 region_model_context
*ctxt
) const final override
2541 const call_details
cd (get_call_details (model
, ctxt
));
2546 model
->update_for_int_cst_return (cd
, 1, true);
2550 /* Return 0; set errno. */
2551 model
->update_for_int_cst_return (cd
, 0, true);
2552 model
->set_errno (cd
);
2555 return feasible_p (cd
, ctxt
);
2559 bool feasible_p (const call_details
&cd
,
2560 region_model_context
*ctxt
) const
2564 /* Can't be "success" on a closed/invalid fd. */
2566 const fd_state_machine
*fd_sm
;
2567 std::unique_ptr
<sm_context
> sm_ctxt
;
2568 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2570 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2574 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
2575 state_machine::state_t old_state
2576 = sm_ctxt
->get_state (cd
.get_call_stmt (), fd_sval
);
2578 if (fd_sm
->is_closed_fd_p (old_state
)
2579 || old_state
== fd_sm
->m_invalid
)
2584 }; // class outcome_of_isatty
2587 bool matches_call_types_p (const call_details
&cd
) const final override
2589 return cd
.num_args () == 1;
2592 void impl_call_post (const call_details
&cd
) const final override
2596 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_isatty
> (cd
, false));
2597 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_isatty
> (cd
, true));
2598 cd
.get_ctxt ()->terminate_path ();
2603 /* Handler for calls to "pipe" and "pipe2".
2604 See e.g. https://www.man7.org/linux/man-pages/man2/pipe.2.html */
2606 class kf_pipe
: public known_function
2608 class failure
: public failed_call_info
2611 failure (const call_details
&cd
) : failed_call_info (cd
) {}
2613 bool update_model (region_model
*model
,
2614 const exploded_edge
*,
2615 region_model_context
*ctxt
) const final override
2617 /* Return -1; everything else is unchanged. */
2618 const call_details
cd (get_call_details (model
, ctxt
));
2619 model
->update_for_int_cst_return (cd
, -1, true);
2624 class success
: public success_call_info
2627 success (const call_details
&cd
) : success_call_info (cd
) {}
2629 bool update_model (region_model
*model
,
2630 const exploded_edge
*,
2631 region_model_context
*ctxt
) const final override
2633 const call_details
cd (get_call_details (model
, ctxt
));
2636 model
->update_for_zero_return (cd
, true);
2638 /* Update fd array. */
2639 region_model_manager
*mgr
= cd
.get_manager ();
2640 tree arr_tree
= cd
.get_arg_tree (0);
2641 const svalue
*arr_sval
= cd
.get_arg_svalue (0);
2642 for (int idx
= 0; idx
< 2; idx
++)
2644 const region
*arr_reg
2645 = model
->deref_rvalue (arr_sval
, arr_tree
, cd
.get_ctxt ());
2646 const svalue
*idx_sval
2647 = mgr
->get_or_create_int_cst (integer_type_node
, idx
);
2648 const region
*element_reg
2649 = mgr
->get_element_region (arr_reg
, integer_type_node
, idx_sval
);
2650 conjured_purge
p (model
, cd
.get_ctxt ());
2651 const svalue
*fd_sval
2652 = mgr
->get_or_create_conjured_svalue (integer_type_node
,
2653 cd
.get_call_stmt (),
2656 model
->set_value (element_reg
, fd_sval
, cd
.get_ctxt ());
2657 model
->mark_as_valid_fd (fd_sval
, cd
.get_ctxt ());
2664 kf_pipe (unsigned num_args
)
2665 : m_num_args (num_args
)
2667 gcc_assert (num_args
> 0);
2670 bool matches_call_types_p (const call_details
&cd
) const final override
2672 return (cd
.num_args () == m_num_args
&& cd
.arg_is_pointer_p (0));
2675 void impl_call_post (const call_details
&cd
) const final override
2679 cd
.get_ctxt ()->bifurcate (make_unique
<failure
> (cd
));
2680 cd
.get_ctxt ()->bifurcate (make_unique
<success
> (cd
));
2681 cd
.get_ctxt ()->terminate_path ();
2686 unsigned m_num_args
;
2689 /* Handler for "read".
2690 ssize_t read(int fildes, void *buf, size_t nbyte);
2691 See e.g. https://man7.org/linux/man-pages/man2/read.2.html */
2693 class kf_read
: public known_function
2696 bool matches_call_types_p (const call_details
&cd
) const final override
2698 return (cd
.num_args () == 3
2699 && cd
.arg_is_pointer_p (1)
2700 && cd
.arg_is_size_p (2));
2703 /* For now, assume that any call to "read" fully clobbers the buffer
2704 passed in. This isn't quite correct (e.g. errors, partial reads;
2705 see PR analyzer/108689), but at least stops us falsely complaining
2706 about the buffer being uninitialized. */
2707 void impl_call_pre (const call_details
&cd
) const final override
2709 region_model
*model
= cd
.get_model ();
2710 const svalue
*ptr_sval
= cd
.get_arg_svalue (1);
2711 if (const region
*reg
= ptr_sval
->maybe_get_region ())
2713 const region
*base_reg
= reg
->get_base_region ();
2714 const svalue
*new_sval
= cd
.get_or_create_conjured_svalue (base_reg
);
2715 model
->set_value (base_reg
, new_sval
, cd
.get_ctxt ());
2717 cd
.set_any_lhs_with_defaults ();
2722 /* Populate KFM with instances of known functions relating to
2723 file descriptors. */
2726 register_known_fd_functions (known_function_manager
&kfm
)
2728 kfm
.add ("accept", make_unique
<kf_accept
> ());
2729 kfm
.add ("bind", make_unique
<kf_bind
> ());
2730 kfm
.add ("connect", make_unique
<kf_connect
> ());
2731 kfm
.add ("isatty", make_unique
<kf_isatty
> ());
2732 kfm
.add ("listen", make_unique
<kf_listen
> ());
2733 kfm
.add ("pipe", make_unique
<kf_pipe
> (1));
2734 kfm
.add ("pipe2", make_unique
<kf_pipe
> (2));
2735 kfm
.add ("read", make_unique
<kf_read
> ());
2736 kfm
.add ("socket", make_unique
<kf_socket
> ());
2741 #endif // ENABLE_ANALYZER