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
24 #include "coretypes.h"
25 #include "make-unique.h"
28 #include "basic-block.h"
31 #include "diagnostic-path.h"
32 #include "analyzer/analyzer.h"
33 #include "diagnostic-event-id.h"
34 #include "analyzer/analyzer-logging.h"
35 #include "analyzer/sm.h"
36 #include "analyzer/pending-diagnostic.h"
37 #include "analyzer/function-set.h"
38 #include "analyzer/analyzer-selftests.h"
39 #include "stringpool.h"
41 #include "analyzer/call-string.h"
42 #include "analyzer/program-point.h"
43 #include "analyzer/store.h"
44 #include "analyzer/region-model.h"
46 #include "analyzer/program-state.h"
47 #include "analyzer/supergraph.h"
48 #include "analyzer/analyzer-language.h"
49 #include "analyzer/call-details.h"
50 #include "analyzer/call-info.h"
58 /* An enum for distinguishing between three different access modes. */
67 enum access_directions
74 /* An enum for distinguishing between dup, dup2 and dup3. */
82 /* Enum for use by -Wanalyzer-fd-phase-mismatch. */
86 EXPECTED_PHASE_CAN_TRANSFER
, /* can "read"/"write". */
87 EXPECTED_PHASE_CAN_BIND
,
88 EXPECTED_PHASE_CAN_LISTEN
,
89 EXPECTED_PHASE_CAN_ACCEPT
,
90 EXPECTED_PHASE_CAN_CONNECT
93 class fd_state_machine
: public state_machine
96 fd_state_machine (logger
*logger
);
99 inherited_state_p () const final override
104 state_machine::state_t
105 get_default_state (const svalue
*sval
) const final override
107 if (tree cst
= sval
->maybe_get_constant ())
109 if (TREE_CODE (cst
) == INTEGER_CST
)
111 int val
= TREE_INT_CST_LOW (cst
);
113 return m_constant_fd
;
121 bool on_stmt (sm_context
*sm_ctxt
, const supernode
*node
,
122 const gimple
*stmt
) const final override
;
124 void on_condition (sm_context
*sm_ctxt
, const supernode
*node
,
125 const gimple
*stmt
, const svalue
*lhs
, const tree_code op
,
126 const svalue
*rhs
) const final override
;
128 bool can_purge_p (state_t s
) const final override
;
129 std::unique_ptr
<pending_diagnostic
> on_leak (tree var
) const final override
;
131 bool is_unchecked_fd_p (state_t s
) const;
132 bool is_valid_fd_p (state_t s
) const;
133 bool is_socket_fd_p (state_t s
) const;
134 bool is_datagram_socket_fd_p (state_t s
) const;
135 bool is_stream_socket_fd_p (state_t s
) const;
136 bool is_closed_fd_p (state_t s
) const;
137 bool is_constant_fd_p (state_t s
) const;
138 bool is_readonly_fd_p (state_t s
) const;
139 bool is_writeonly_fd_p (state_t s
) const;
140 enum access_mode
get_access_mode_from_flag (int flag
) const;
141 /* Function for one-to-one correspondence between valid
142 and unchecked states. */
143 state_t
valid_to_unchecked_state (state_t state
) const;
145 void mark_as_valid_fd (region_model
*model
,
147 const svalue
*fd_sval
,
148 const extrinsic_state
&ext_state
) const;
150 bool on_socket (const call_details
&cd
,
153 const extrinsic_state
&ext_state
) const;
154 bool on_bind (const call_details
&cd
,
157 const extrinsic_state
&ext_state
) const;
158 bool on_listen (const call_details
&cd
,
161 const extrinsic_state
&ext_state
) const;
162 bool on_accept (const call_details
&cd
,
165 const extrinsic_state
&ext_state
) const;
166 bool on_connect (const call_details
&cd
,
169 const extrinsic_state
&ext_state
) const;
171 /* State for a constant file descriptor (>= 0) */
172 state_t m_constant_fd
;
174 /* States representing a file descriptor that hasn't yet been
175 checked for validity after opening, for three different
177 state_t m_unchecked_read_write
;
179 state_t m_unchecked_read_only
;
181 state_t m_unchecked_write_only
;
183 /* States for representing a file descriptor that is known to be valid (>=
184 0), for three different access modes. */
185 state_t m_valid_read_write
;
187 state_t m_valid_read_only
;
189 state_t m_valid_write_only
;
191 /* State for a file descriptor that is known to be invalid (< 0). */
194 /* State for a file descriptor that has been closed. */
197 /* States for FDs relating to socket APIs. */
199 /* Result of successful "socket" with SOCK_DGRAM. */
200 state_t m_new_datagram_socket
;
201 /* Result of successful "socket" with SOCK_STREAM. */
202 state_t m_new_stream_socket
;
203 /* Result of successful "socket" with unknown type. */
204 state_t m_new_unknown_socket
;
206 /* The above after a successful call to "bind". */
207 state_t m_bound_datagram_socket
;
208 state_t m_bound_stream_socket
;
209 state_t m_bound_unknown_socket
;
211 /* A bound socket after a successful call to "listen" (stream or unknown). */
212 state_t m_listening_stream_socket
;
214 /* (i) the new FD as a result of a succesful call to "accept" on a
215 listening socket (via a passive open), or
216 (ii) an active socket after a successful call to "connect"
217 (via an active open). */
218 state_t m_connected_stream_socket
;
220 /* State for a file descriptor that we do not want to track anymore . */
223 /* Stashed constant values from the frontend. These could be NULL. */
231 void on_open (sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
232 const gcall
*call
) const;
233 void on_creat (sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
234 const gcall
*call
) const;
235 void on_close (sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
236 const gcall
*call
) const;
237 void on_read (sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
238 const gcall
*call
, const tree callee_fndecl
) const;
239 void on_write (sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
240 const gcall
*call
, const tree callee_fndecl
) const;
241 void check_for_open_fd (sm_context
*sm_ctxt
, const supernode
*node
,
242 const gimple
*stmt
, const gcall
*call
,
243 const tree callee_fndecl
,
244 enum access_directions access_fn
) const;
246 void make_valid_transitions_on_condition (sm_context
*sm_ctxt
,
247 const supernode
*node
,
249 const svalue
*lhs
) const;
250 void make_invalid_transitions_on_condition (sm_context
*sm_ctxt
,
251 const supernode
*node
,
253 const svalue
*lhs
) const;
254 void check_for_fd_attrs (sm_context
*sm_ctxt
, const supernode
*node
,
255 const gimple
*stmt
, const gcall
*call
,
256 const tree callee_fndecl
, const char *attr_name
,
257 access_directions fd_attr_access_dir
) const;
258 void check_for_dup (sm_context
*sm_ctxt
, const supernode
*node
,
259 const gimple
*stmt
, const gcall
*call
, const tree callee_fndecl
,
260 enum dup kind
) const;
262 state_t
get_state_for_socket_type (const svalue
*socket_type_sval
) const;
264 bool check_for_socket_fd (const call_details
&cd
,
267 const svalue
*fd_sval
,
268 const supernode
*node
,
270 bool *complained
= NULL
) const;
271 bool check_for_new_socket_fd (const call_details
&cd
,
274 const svalue
*fd_sval
,
275 const supernode
*node
,
277 enum expected_phase expected_phase
) const;
280 /* Base diagnostic class relative to fd_state_machine. */
281 class fd_diagnostic
: public pending_diagnostic
284 fd_diagnostic (const fd_state_machine
&sm
, tree arg
) : m_sm (sm
), m_arg (arg
)
289 subclass_equal_p (const pending_diagnostic
&base_other
) const override
291 return same_tree_p (m_arg
, ((const fd_diagnostic
&)base_other
).m_arg
);
295 describe_state_change (const evdesc::state_change
&change
) override
297 if (change
.m_old_state
== m_sm
.get_start_state ())
299 if (change
.m_new_state
== m_sm
.m_unchecked_read_write
300 || change
.m_new_state
== m_sm
.m_valid_read_write
)
301 return change
.formatted_print ("opened here as read-write");
303 if (change
.m_new_state
== m_sm
.m_unchecked_read_only
304 || change
.m_new_state
== m_sm
.m_valid_read_only
)
305 return change
.formatted_print ("opened here as read-only");
307 if (change
.m_new_state
== m_sm
.m_unchecked_write_only
308 || change
.m_new_state
== m_sm
.m_valid_write_only
)
309 return change
.formatted_print ("opened here as write-only");
311 if (change
.m_new_state
== m_sm
.m_new_datagram_socket
)
312 return change
.formatted_print ("datagram socket created here");
314 if (change
.m_new_state
== m_sm
.m_new_stream_socket
)
315 return change
.formatted_print ("stream socket created here");
317 if (change
.m_new_state
== m_sm
.m_new_unknown_socket
318 || change
.m_new_state
== m_sm
.m_connected_stream_socket
)
319 return change
.formatted_print ("socket created here");
322 if (change
.m_new_state
== m_sm
.m_bound_datagram_socket
)
323 return change
.formatted_print ("datagram socket bound here");
325 if (change
.m_new_state
== m_sm
.m_bound_stream_socket
)
326 return change
.formatted_print ("stream socket bound here");
328 if (change
.m_new_state
== m_sm
.m_bound_unknown_socket
329 || change
.m_new_state
== m_sm
.m_connected_stream_socket
)
330 return change
.formatted_print ("socket bound here");
332 if (change
.m_new_state
== m_sm
.m_listening_stream_socket
)
333 return change
.formatted_print
334 ("stream socket marked as passive here via %qs", "listen");
336 if (change
.m_new_state
== m_sm
.m_closed
)
337 return change
.formatted_print ("closed here");
339 if (m_sm
.is_unchecked_fd_p (change
.m_old_state
)
340 && m_sm
.is_valid_fd_p (change
.m_new_state
))
343 return change
.formatted_print (
344 "assuming %qE is a valid file descriptor (>= 0)", change
.m_expr
);
346 return change
.formatted_print ("assuming a valid file descriptor");
349 if (m_sm
.is_unchecked_fd_p (change
.m_old_state
)
350 && change
.m_new_state
== m_sm
.m_invalid
)
353 return change
.formatted_print (
354 "assuming %qE is an invalid file descriptor (< 0)",
357 return change
.formatted_print ("assuming an invalid file descriptor");
360 return label_text ();
363 diagnostic_event::meaning
364 get_meaning_for_state_change (
365 const evdesc::state_change
&change
) const final override
367 if (change
.m_old_state
== m_sm
.get_start_state ()
368 && (m_sm
.is_unchecked_fd_p (change
.m_new_state
)
369 || change
.m_new_state
== m_sm
.m_new_datagram_socket
370 || change
.m_new_state
== m_sm
.m_new_stream_socket
371 || change
.m_new_state
== m_sm
.m_new_unknown_socket
))
372 return diagnostic_event::meaning (diagnostic_event::VERB_acquire
,
373 diagnostic_event::NOUN_resource
);
374 if (change
.m_new_state
== m_sm
.m_closed
)
375 return diagnostic_event::meaning (diagnostic_event::VERB_release
,
376 diagnostic_event::NOUN_resource
);
377 return diagnostic_event::meaning ();
381 const fd_state_machine
&m_sm
;
385 class fd_param_diagnostic
: public fd_diagnostic
388 fd_param_diagnostic (const fd_state_machine
&sm
, tree arg
, tree callee_fndecl
,
389 const char *attr_name
, int arg_idx
)
390 : fd_diagnostic (sm
, arg
), m_callee_fndecl (callee_fndecl
),
391 m_attr_name (attr_name
), m_arg_idx (arg_idx
)
395 fd_param_diagnostic (const fd_state_machine
&sm
, tree arg
, tree callee_fndecl
)
396 : fd_diagnostic (sm
, arg
), m_callee_fndecl (callee_fndecl
),
397 m_attr_name (NULL
), m_arg_idx (-1)
402 subclass_equal_p (const pending_diagnostic
&base_other
) const override
404 const fd_param_diagnostic
&sub_other
405 = (const fd_param_diagnostic
&)base_other
;
406 return (same_tree_p (m_arg
, sub_other
.m_arg
)
407 && same_tree_p (m_callee_fndecl
, sub_other
.m_callee_fndecl
)
408 && m_arg_idx
== sub_other
.m_arg_idx
410 ? (strcmp (m_attr_name
, sub_other
.m_attr_name
) == 0)
415 inform_filedescriptor_attribute (access_directions fd_dir
)
421 case DIRS_READ_WRITE
:
422 inform (DECL_SOURCE_LOCATION (m_callee_fndecl
),
423 "argument %d of %qD must be an open file descriptor, due to "
424 "%<__attribute__((%s(%d)))%>",
425 m_arg_idx
+ 1, m_callee_fndecl
, m_attr_name
, m_arg_idx
+ 1);
428 inform (DECL_SOURCE_LOCATION (m_callee_fndecl
),
429 "argument %d of %qD must be a readable file descriptor, due "
430 "to %<__attribute__((%s(%d)))%>",
431 m_arg_idx
+ 1, m_callee_fndecl
, m_attr_name
, m_arg_idx
+ 1);
434 inform (DECL_SOURCE_LOCATION (m_callee_fndecl
),
435 "argument %d of %qD must be a writable file descriptor, due "
436 "to %<__attribute__((%s(%d)))%>",
437 m_arg_idx
+ 1, m_callee_fndecl
, m_attr_name
, m_arg_idx
+ 1);
443 tree m_callee_fndecl
;
444 const char *m_attr_name
;
445 /* ARG_IDX is 0-based. */
449 class fd_leak
: public fd_diagnostic
452 fd_leak (const fd_state_machine
&sm
, tree arg
) : fd_diagnostic (sm
, arg
) {}
455 get_kind () const final override
461 get_controlling_option () const final override
463 return OPT_Wanalyzer_fd_leak
;
467 emit (diagnostic_emission_context
&ctxt
) final override
469 /*CWE-775: Missing Release of File Descriptor or Handle after Effective
474 return ctxt
.warn ("leak of file descriptor %qE", m_arg
);
476 return ctxt
.warn ("leak of file descriptor");
480 describe_state_change (const evdesc::state_change
&change
) final override
482 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
484 m_open_event
= change
.m_event_id
;
485 return label_text::borrow ("opened here");
488 return fd_diagnostic::describe_state_change (change
);
492 describe_final_event (const evdesc::final_event
&ev
) final override
494 if (m_open_event
.known_p ())
497 return ev
.formatted_print ("%qE leaks here; was opened at %@",
498 ev
.m_expr
, &m_open_event
);
500 return ev
.formatted_print ("leaks here; was opened at %@",
506 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
508 return ev
.formatted_print ("leaks here");
513 diagnostic_event_id_t m_open_event
;
516 class fd_access_mode_mismatch
: public fd_param_diagnostic
519 fd_access_mode_mismatch (const fd_state_machine
&sm
, tree arg
,
520 enum access_directions fd_dir
,
521 const tree callee_fndecl
, const char *attr_name
,
523 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
),
529 fd_access_mode_mismatch (const fd_state_machine
&sm
, tree arg
,
530 enum access_directions fd_dir
,
531 const tree callee_fndecl
)
532 : fd_param_diagnostic (sm
, arg
, callee_fndecl
), m_fd_dir (fd_dir
)
537 get_kind () const final override
539 return "fd_access_mode_mismatch";
543 get_controlling_option () const final override
545 return OPT_Wanalyzer_fd_access_mode_mismatch
;
549 emit (diagnostic_emission_context
&ctxt
) final override
555 warned
= ctxt
.warn ("%qE on read-only file descriptor %qE",
556 m_callee_fndecl
, m_arg
);
559 warned
= ctxt
.warn ("%qE on write-only file descriptor %qE",
560 m_callee_fndecl
, m_arg
);
566 inform_filedescriptor_attribute (m_fd_dir
);
571 describe_final_event (const evdesc::final_event
&ev
) final override
576 return ev
.formatted_print ("%qE on read-only file descriptor %qE",
577 m_callee_fndecl
, m_arg
);
579 return ev
.formatted_print ("%qE on write-only file descriptor %qE",
580 m_callee_fndecl
, m_arg
);
587 enum access_directions m_fd_dir
;
590 class fd_double_close
: public fd_diagnostic
593 fd_double_close (const fd_state_machine
&sm
, tree arg
) : fd_diagnostic (sm
, arg
)
598 get_kind () const final override
600 return "fd_double_close";
604 get_controlling_option () const final override
606 return OPT_Wanalyzer_fd_double_close
;
609 emit (diagnostic_emission_context
&ctxt
) final override
611 // CWE-1341: Multiple Releases of Same Resource or Handle
613 return ctxt
.warn ("double %<close%> of file descriptor %qE", m_arg
);
617 describe_state_change (const evdesc::state_change
&change
) override
619 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
620 return label_text::borrow ("opened here");
622 if (change
.m_new_state
== m_sm
.m_closed
)
624 m_first_close_event
= change
.m_event_id
;
625 return change
.formatted_print ("first %qs here", "close");
627 return fd_diagnostic::describe_state_change (change
);
631 describe_final_event (const evdesc::final_event
&ev
) final override
633 if (m_first_close_event
.known_p ())
634 return ev
.formatted_print ("second %qs here; first %qs was at %@",
635 "close", "close", &m_first_close_event
);
636 return ev
.formatted_print ("second %qs here", "close");
640 diagnostic_event_id_t m_first_close_event
;
643 class fd_use_after_close
: public fd_param_diagnostic
646 fd_use_after_close (const fd_state_machine
&sm
, tree arg
,
647 const tree callee_fndecl
, const char *attr_name
,
649 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
)
653 fd_use_after_close (const fd_state_machine
&sm
, tree arg
,
654 const tree callee_fndecl
)
655 : fd_param_diagnostic (sm
, arg
, callee_fndecl
)
660 get_kind () const final override
662 return "fd_use_after_close";
666 get_controlling_option () const final override
668 return OPT_Wanalyzer_fd_use_after_close
;
672 emit (diagnostic_emission_context
&ctxt
) final override
674 bool warned
= ctxt
.warn ("%qE on closed file descriptor %qE",
675 m_callee_fndecl
, m_arg
);
677 inform_filedescriptor_attribute (DIRS_READ_WRITE
);
682 describe_state_change (const evdesc::state_change
&change
) override
684 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
685 return label_text::borrow ("opened here");
687 if (change
.m_new_state
== m_sm
.m_closed
)
689 m_first_close_event
= change
.m_event_id
;
690 return change
.formatted_print ("closed here");
693 return fd_diagnostic::describe_state_change (change
);
697 describe_final_event (const evdesc::final_event
&ev
) final override
699 if (m_first_close_event
.known_p ())
700 return ev
.formatted_print (
701 "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl
,
702 m_arg
, "close", &m_first_close_event
);
704 return ev
.formatted_print ("%qE on closed file descriptor %qE",
705 m_callee_fndecl
, m_arg
);
709 diagnostic_event_id_t m_first_close_event
;
712 class fd_use_without_check
: public fd_param_diagnostic
715 fd_use_without_check (const fd_state_machine
&sm
, tree arg
,
716 const tree callee_fndecl
, const char *attr_name
,
718 : fd_param_diagnostic (sm
, arg
, callee_fndecl
, attr_name
, arg_idx
)
722 fd_use_without_check (const fd_state_machine
&sm
, tree arg
,
723 const tree callee_fndecl
)
724 : fd_param_diagnostic (sm
, arg
, callee_fndecl
)
729 get_kind () const final override
731 return "fd_use_without_check";
735 get_controlling_option () const final override
737 return OPT_Wanalyzer_fd_use_without_check
;
741 emit (diagnostic_emission_context
&ctxt
) final override
743 bool warned
= ctxt
.warn ("%qE on possibly invalid file descriptor %qE",
744 m_callee_fndecl
, m_arg
);
746 inform_filedescriptor_attribute (DIRS_READ_WRITE
);
751 describe_state_change (const evdesc::state_change
&change
) override
753 if (m_sm
.is_unchecked_fd_p (change
.m_new_state
))
755 m_first_open_event
= change
.m_event_id
;
756 return label_text::borrow ("opened here");
759 return fd_diagnostic::describe_state_change (change
);
763 describe_final_event (const evdesc::final_event
&ev
) final override
765 if (m_first_open_event
.known_p ())
766 return ev
.formatted_print (
767 "%qE could be invalid: unchecked value from %@", m_arg
,
768 &m_first_open_event
);
770 return ev
.formatted_print ("%qE could be invalid", m_arg
);
774 diagnostic_event_id_t m_first_open_event
;
777 /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch. */
779 class fd_phase_mismatch
: public fd_param_diagnostic
782 fd_phase_mismatch (const fd_state_machine
&sm
, tree arg
,
783 const tree callee_fndecl
,
784 state_machine::state_t actual_state
,
785 enum expected_phase expected_phase
)
786 : fd_param_diagnostic (sm
, arg
, callee_fndecl
),
787 m_actual_state (actual_state
),
788 m_expected_phase (expected_phase
)
790 gcc_assert (m_sm
.is_socket_fd_p (actual_state
));
791 switch (expected_phase
)
793 case EXPECTED_PHASE_CAN_TRANSFER
:
794 gcc_assert (actual_state
== m_sm
.m_new_stream_socket
795 || actual_state
== m_sm
.m_bound_stream_socket
796 || actual_state
== m_sm
.m_listening_stream_socket
);
798 case EXPECTED_PHASE_CAN_BIND
:
799 gcc_assert (actual_state
== m_sm
.m_bound_datagram_socket
800 || actual_state
== m_sm
.m_bound_stream_socket
801 || actual_state
== m_sm
.m_bound_unknown_socket
802 || actual_state
== m_sm
.m_connected_stream_socket
803 || actual_state
== m_sm
.m_listening_stream_socket
);
805 case EXPECTED_PHASE_CAN_LISTEN
:
806 gcc_assert (actual_state
== m_sm
.m_new_stream_socket
807 || actual_state
== m_sm
.m_new_unknown_socket
808 || actual_state
== m_sm
.m_connected_stream_socket
);
810 case EXPECTED_PHASE_CAN_ACCEPT
:
811 gcc_assert (actual_state
== m_sm
.m_new_stream_socket
812 || actual_state
== m_sm
.m_new_unknown_socket
813 || actual_state
== m_sm
.m_bound_stream_socket
814 || actual_state
== m_sm
.m_bound_unknown_socket
815 || actual_state
== m_sm
.m_connected_stream_socket
);
817 case EXPECTED_PHASE_CAN_CONNECT
:
818 gcc_assert (actual_state
== m_sm
.m_bound_datagram_socket
819 || actual_state
== m_sm
.m_bound_stream_socket
820 || actual_state
== m_sm
.m_bound_unknown_socket
821 || actual_state
== m_sm
.m_listening_stream_socket
822 || actual_state
== m_sm
.m_connected_stream_socket
);
828 get_kind () const final override
830 return "fd_phase_mismatch";
834 subclass_equal_p (const pending_diagnostic
&base_other
) const final override
836 const fd_phase_mismatch
&sub_other
= (const fd_phase_mismatch
&)base_other
;
837 if (!fd_param_diagnostic ::subclass_equal_p (sub_other
))
839 return (m_actual_state
== sub_other
.m_actual_state
840 && m_expected_phase
== sub_other
.m_expected_phase
);
844 get_controlling_option () const final override
846 return OPT_Wanalyzer_fd_phase_mismatch
;
850 emit (diagnostic_emission_context
&ctxt
) final override
852 /* CWE-666: Operation on Resource in Wrong Phase of Lifetime. */
854 return ctxt
.warn ("%qE on file descriptor %qE in wrong phase",
855 m_callee_fndecl
, m_arg
);
859 describe_final_event (const evdesc::final_event
&ev
) final override
861 switch (m_expected_phase
)
863 case EXPECTED_PHASE_CAN_TRANSFER
:
865 if (m_actual_state
== m_sm
.m_new_stream_socket
)
866 return ev
.formatted_print
867 ("%qE expects a stream socket to be connected via %qs"
868 " but %qE has not yet been bound",
869 m_callee_fndecl
, "accept", m_arg
);
870 if (m_actual_state
== m_sm
.m_bound_stream_socket
)
871 return ev
.formatted_print
872 ("%qE expects a stream socket to be connected via %qs"
873 " but %qE is not yet listening",
874 m_callee_fndecl
, "accept", m_arg
);
875 if (m_actual_state
== m_sm
.m_listening_stream_socket
)
876 return ev
.formatted_print
877 ("%qE expects a stream socket to be connected via"
878 " the return value of %qs"
879 " but %qE is listening; wrong file descriptor?",
880 m_callee_fndecl
, "accept", m_arg
);
883 case EXPECTED_PHASE_CAN_BIND
:
885 if (m_actual_state
== m_sm
.m_bound_datagram_socket
886 || m_actual_state
== m_sm
.m_bound_stream_socket
887 || m_actual_state
== m_sm
.m_bound_unknown_socket
)
888 return ev
.formatted_print
889 ("%qE expects a new socket file descriptor"
890 " but %qE has already been bound",
891 m_callee_fndecl
, m_arg
);
892 if (m_actual_state
== m_sm
.m_connected_stream_socket
)
893 return ev
.formatted_print
894 ("%qE expects a new socket file descriptor"
895 " but %qE is already connected",
896 m_callee_fndecl
, m_arg
);
897 if (m_actual_state
== m_sm
.m_listening_stream_socket
)
898 return ev
.formatted_print
899 ("%qE expects a new socket file descriptor"
900 " but %qE is already listening",
901 m_callee_fndecl
, m_arg
);
904 case EXPECTED_PHASE_CAN_LISTEN
:
906 if (m_actual_state
== m_sm
.m_new_stream_socket
907 || m_actual_state
== m_sm
.m_new_unknown_socket
)
908 return ev
.formatted_print
909 ("%qE expects a bound stream socket file descriptor"
910 " but %qE has not yet been bound",
911 m_callee_fndecl
, m_arg
);
912 if (m_actual_state
== m_sm
.m_connected_stream_socket
)
913 return ev
.formatted_print
914 ("%qE expects a bound stream socket file descriptor"
915 " but %qE is connected",
916 m_callee_fndecl
, m_arg
);
919 case EXPECTED_PHASE_CAN_ACCEPT
:
921 if (m_actual_state
== m_sm
.m_new_stream_socket
922 || m_actual_state
== m_sm
.m_new_unknown_socket
)
923 return ev
.formatted_print
924 ("%qE expects a listening stream socket file descriptor"
925 " but %qE has not yet been bound",
926 m_callee_fndecl
, m_arg
);
927 if (m_actual_state
== m_sm
.m_bound_stream_socket
928 || m_actual_state
== m_sm
.m_bound_unknown_socket
)
929 return ev
.formatted_print
930 ("%qE expects a listening stream socket file descriptor"
931 " whereas %qE is bound but not yet listening",
932 m_callee_fndecl
, m_arg
);
933 if (m_actual_state
== m_sm
.m_connected_stream_socket
)
934 return ev
.formatted_print
935 ("%qE expects a listening stream socket file descriptor"
936 " but %qE is connected",
937 m_callee_fndecl
, m_arg
);
940 case EXPECTED_PHASE_CAN_CONNECT
:
942 if (m_actual_state
== m_sm
.m_bound_datagram_socket
943 || m_actual_state
== m_sm
.m_bound_stream_socket
944 || m_actual_state
== m_sm
.m_bound_unknown_socket
)
945 return ev
.formatted_print
946 ("%qE expects a new socket file descriptor but %qE is bound",
947 m_callee_fndecl
, m_arg
);
949 return ev
.formatted_print
950 ("%qE expects a new socket file descriptor", m_callee_fndecl
);
958 state_machine::state_t m_actual_state
;
959 enum expected_phase m_expected_phase
;
962 /* Enum for use by -Wanalyzer-fd-type-mismatch. */
966 EXPECTED_TYPE_SOCKET
,
967 EXPECTED_TYPE_STREAM_SOCKET
970 /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-type-mismatch. */
972 class fd_type_mismatch
: public fd_param_diagnostic
975 fd_type_mismatch (const fd_state_machine
&sm
, tree arg
,
976 const tree callee_fndecl
,
977 state_machine::state_t actual_state
,
978 enum expected_type expected_type
)
979 : fd_param_diagnostic (sm
, arg
, callee_fndecl
),
980 m_actual_state (actual_state
),
981 m_expected_type (expected_type
)
986 get_kind () const final override
988 return "fd_type_mismatch";
992 subclass_equal_p (const pending_diagnostic
&base_other
) const final override
994 const fd_type_mismatch
&sub_other
= (const fd_type_mismatch
&)base_other
;
995 if (!fd_param_diagnostic ::subclass_equal_p (sub_other
))
997 return (m_actual_state
== sub_other
.m_actual_state
998 && m_expected_type
== sub_other
.m_expected_type
);
1002 get_controlling_option () const final override
1004 return OPT_Wanalyzer_fd_type_mismatch
;
1008 emit (diagnostic_emission_context
&ctxt
) final override
1010 switch (m_expected_type
)
1014 case EXPECTED_TYPE_SOCKET
:
1015 return ctxt
.warn ("%qE on non-socket file descriptor %qE",
1016 m_callee_fndecl
, m_arg
);
1017 case EXPECTED_TYPE_STREAM_SOCKET
:
1018 if (m_sm
.is_datagram_socket_fd_p (m_actual_state
))
1019 return ctxt
.warn ("%qE on datagram socket file descriptor %qE",
1020 m_callee_fndecl
, m_arg
);
1022 return ctxt
.warn ("%qE on non-stream-socket file descriptor %qE",
1023 m_callee_fndecl
, m_arg
);
1028 describe_final_event (const evdesc::final_event
&ev
) final override
1030 switch (m_expected_type
)
1035 case EXPECTED_TYPE_SOCKET
:
1036 case EXPECTED_TYPE_STREAM_SOCKET
:
1037 if (!m_sm
.is_socket_fd_p (m_actual_state
))
1038 return ev
.formatted_print ("%qE expects a socket file descriptor"
1039 " but %qE is not a socket",
1040 m_callee_fndecl
, m_arg
);
1042 gcc_assert (m_expected_type
== EXPECTED_TYPE_STREAM_SOCKET
);
1043 gcc_assert (m_sm
.is_datagram_socket_fd_p (m_actual_state
));
1044 return ev
.formatted_print
1045 ("%qE expects a stream socket file descriptor"
1046 " but %qE is a datagram socket",
1047 m_callee_fndecl
, m_arg
);
1051 state_machine::state_t m_actual_state
;
1052 enum expected_type m_expected_type
;
1055 fd_state_machine::fd_state_machine (logger
*logger
)
1056 : state_machine ("file-descriptor", logger
),
1057 m_constant_fd (add_state ("fd-constant")),
1058 m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
1059 m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
1060 m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
1061 m_valid_read_write (add_state ("fd-valid-read-write")),
1062 m_valid_read_only (add_state ("fd-valid-read-only")),
1063 m_valid_write_only (add_state ("fd-valid-write-only")),
1064 m_invalid (add_state ("fd-invalid")),
1065 m_closed (add_state ("fd-closed")),
1066 m_new_datagram_socket (add_state ("fd-new-datagram-socket")),
1067 m_new_stream_socket (add_state ("fd-new-stream-socket")),
1068 m_new_unknown_socket (add_state ("fd-new-unknown-socket")),
1069 m_bound_datagram_socket (add_state ("fd-bound-datagram-socket")),
1070 m_bound_stream_socket (add_state ("fd-bound-stream-socket")),
1071 m_bound_unknown_socket (add_state ("fd-bound-unknown-socket")),
1072 m_listening_stream_socket (add_state ("fd-listening-stream-socket")),
1073 m_connected_stream_socket (add_state ("fd-connected-stream-socket")),
1074 m_stop (add_state ("fd-stop")),
1075 m_O_ACCMODE (get_stashed_constant_by_name ("O_ACCMODE")),
1076 m_O_RDONLY (get_stashed_constant_by_name ("O_RDONLY")),
1077 m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY")),
1078 m_SOCK_STREAM (get_stashed_constant_by_name ("SOCK_STREAM")),
1079 m_SOCK_DGRAM (get_stashed_constant_by_name ("SOCK_DGRAM"))
1084 fd_state_machine::is_unchecked_fd_p (state_t s
) const
1086 return (s
== m_unchecked_read_write
1087 || s
== m_unchecked_read_only
1088 || s
== m_unchecked_write_only
);
1092 fd_state_machine::is_valid_fd_p (state_t s
) const
1094 return (s
== m_valid_read_write
1095 || s
== m_valid_read_only
1096 || s
== m_valid_write_only
);
1100 fd_state_machine::is_socket_fd_p (state_t s
) const
1102 return (s
== m_new_datagram_socket
1103 || s
== m_new_stream_socket
1104 || s
== m_new_unknown_socket
1105 || s
== m_bound_datagram_socket
1106 || s
== m_bound_stream_socket
1107 || s
== m_bound_unknown_socket
1108 || s
== m_listening_stream_socket
1109 || s
== m_connected_stream_socket
);
1113 fd_state_machine::is_datagram_socket_fd_p (state_t s
) const
1115 return (s
== m_new_datagram_socket
1116 || s
== m_new_unknown_socket
1117 || s
== m_bound_datagram_socket
1118 || s
== m_bound_unknown_socket
);
1122 fd_state_machine::is_stream_socket_fd_p (state_t s
) const
1124 return (s
== m_new_stream_socket
1125 || s
== m_new_unknown_socket
1126 || s
== m_bound_stream_socket
1127 || s
== m_bound_unknown_socket
1128 || s
== m_listening_stream_socket
1129 || s
== m_connected_stream_socket
);
1133 fd_state_machine::get_access_mode_from_flag (int flag
) const
1135 if (m_O_ACCMODE
&& TREE_CODE (m_O_ACCMODE
) == INTEGER_CST
)
1137 const unsigned HOST_WIDE_INT mask_val
= TREE_INT_CST_LOW (m_O_ACCMODE
);
1138 const unsigned HOST_WIDE_INT masked_flag
= flag
& mask_val
;
1140 if (m_O_RDONLY
&& TREE_CODE (m_O_RDONLY
) == INTEGER_CST
)
1141 if (masked_flag
== TREE_INT_CST_LOW (m_O_RDONLY
))
1144 if (m_O_WRONLY
&& TREE_CODE (m_O_WRONLY
) == INTEGER_CST
)
1145 if (masked_flag
== TREE_INT_CST_LOW (m_O_WRONLY
))
1152 fd_state_machine::is_readonly_fd_p (state_t state
) const
1154 return (state
== m_unchecked_read_only
|| state
== m_valid_read_only
);
1158 fd_state_machine::is_writeonly_fd_p (state_t state
) const
1160 return (state
== m_unchecked_write_only
|| state
== m_valid_write_only
);
1164 fd_state_machine::is_closed_fd_p (state_t state
) const
1166 return (state
== m_closed
);
1170 fd_state_machine::is_constant_fd_p (state_t state
) const
1172 return (state
== m_constant_fd
);
1175 fd_state_machine::state_t
1176 fd_state_machine::valid_to_unchecked_state (state_t state
) const
1178 if (state
== m_valid_read_write
)
1179 return m_unchecked_read_write
;
1180 else if (state
== m_valid_write_only
)
1181 return m_unchecked_write_only
;
1182 else if (state
== m_valid_read_only
)
1183 return m_unchecked_read_only
;
1190 fd_state_machine::mark_as_valid_fd (region_model
*model
,
1192 const svalue
*fd_sval
,
1193 const extrinsic_state
&ext_state
) const
1195 smap
->set_state (model
, fd_sval
, m_valid_read_write
, NULL
, ext_state
);
1199 fd_state_machine::on_stmt (sm_context
*sm_ctxt
, const supernode
*node
,
1200 const gimple
*stmt
) const
1202 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1203 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
1205 if (is_named_call_p (callee_fndecl
, "open", call
, 2))
1207 on_open (sm_ctxt
, node
, stmt
, call
);
1211 if (is_named_call_p (callee_fndecl
, "creat", call
, 2))
1213 on_creat (sm_ctxt
, node
, stmt
, call
);
1217 if (is_named_call_p (callee_fndecl
, "close", call
, 1))
1219 on_close (sm_ctxt
, node
, stmt
, call
);
1223 if (is_named_call_p (callee_fndecl
, "write", call
, 3))
1225 on_write (sm_ctxt
, node
, stmt
, call
, callee_fndecl
);
1229 if (is_named_call_p (callee_fndecl
, "read", call
, 3))
1231 on_read (sm_ctxt
, node
, stmt
, call
, callee_fndecl
);
1235 if (is_named_call_p (callee_fndecl
, "dup", call
, 1))
1237 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_1
);
1241 if (is_named_call_p (callee_fndecl
, "dup2", call
, 2))
1243 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_2
);
1247 if (is_named_call_p (callee_fndecl
, "dup3", call
, 3))
1249 check_for_dup (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DUP_3
);
1254 // Handle __attribute__((fd_arg))
1256 check_for_fd_attrs (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1257 "fd_arg", DIRS_READ_WRITE
);
1259 // Handle __attribute__((fd_arg_read))
1261 check_for_fd_attrs (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1262 "fd_arg_read", DIRS_READ
);
1264 // Handle __attribute__((fd_arg_write))
1266 check_for_fd_attrs (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1267 "fd_arg_write", DIRS_WRITE
);
1275 fd_state_machine::check_for_fd_attrs (
1276 sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
1277 const gcall
*call
, const tree callee_fndecl
, const char *attr_name
,
1278 access_directions fd_attr_access_dir
) const
1280 /* Handle interesting fd attributes of the callee_fndecl,
1281 or prioritize those of the builtin that callee_fndecl is
1283 Might want this to be controlled by a flag. */
1284 tree fndecl
= callee_fndecl
;
1285 /* If call is recognized as a builtin known_function,
1286 use that builtin's function_decl. */
1287 if (const region_model
*old_model
= sm_ctxt
->get_old_region_model ())
1288 if (const builtin_known_function
*builtin_kf
1289 = old_model
->get_builtin_kf (call
))
1290 fndecl
= builtin_kf
->builtin_decl ();
1292 tree attrs
= TYPE_ATTRIBUTES (TREE_TYPE (fndecl
));
1293 attrs
= lookup_attribute (attr_name
, attrs
);
1297 if (!TREE_VALUE (attrs
))
1302 for (tree idx
= TREE_VALUE (attrs
); idx
; idx
= TREE_CHAIN (idx
))
1304 unsigned int val
= TREE_INT_CST_LOW (TREE_VALUE (idx
)) - 1;
1305 bitmap_set_bit (argmap
, val
);
1307 if (bitmap_empty_p (argmap
))
1310 for (unsigned arg_idx
= 0; arg_idx
< gimple_call_num_args (call
); arg_idx
++)
1312 tree arg
= gimple_call_arg (call
, arg_idx
);
1313 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1314 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1315 bool bit_set
= bitmap_bit_p (argmap
, arg_idx
);
1316 if (TREE_CODE (TREE_TYPE (arg
)) != INTEGER_TYPE
)
1318 if (bit_set
) // Check if arg_idx is marked by any of the file descriptor
1322 /* Do use the fndecl that caused the warning so that the
1323 misused attributes are printed and the user not confused. */
1324 if (is_closed_fd_p (state
))
1327 sm_ctxt
->warn (node
, stmt
, arg
,
1328 make_unique
<fd_use_after_close
>
1335 if (!(is_valid_fd_p (state
) || (state
== m_stop
)))
1337 if (!is_constant_fd_p (state
))
1339 sm_ctxt
->warn (node
, stmt
, arg
,
1340 make_unique
<fd_use_without_check
>
1348 switch (fd_attr_access_dir
)
1350 case DIRS_READ_WRITE
:
1354 if (is_writeonly_fd_p (state
))
1358 make_unique
<fd_access_mode_mismatch
> (*this, diag_arg
,
1368 if (is_readonly_fd_p (state
))
1372 make_unique
<fd_access_mode_mismatch
> (*this, diag_arg
,
1387 fd_state_machine::on_open (sm_context
*sm_ctxt
, const supernode
*node
,
1388 const gimple
*stmt
, const gcall
*call
) const
1390 tree lhs
= gimple_call_lhs (call
);
1393 tree arg
= gimple_call_arg (call
, 1);
1394 enum access_mode mode
= READ_WRITE
;
1395 if (TREE_CODE (arg
) == INTEGER_CST
)
1397 int flag
= TREE_INT_CST_LOW (arg
);
1398 mode
= get_access_mode_from_flag (flag
);
1403 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
,
1404 m_unchecked_read_only
);
1407 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
,
1408 m_unchecked_write_only
);
1411 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
,
1412 m_unchecked_read_write
);
1417 sm_ctxt
->warn (node
, stmt
, NULL_TREE
,
1418 make_unique
<fd_leak
> (*this, NULL_TREE
));
1423 fd_state_machine::on_creat (sm_context
*sm_ctxt
, const supernode
*node
,
1424 const gimple
*stmt
, const gcall
*call
) const
1426 tree lhs
= gimple_call_lhs (call
);
1428 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_unchecked_write_only
);
1430 sm_ctxt
->warn (node
, stmt
, NULL_TREE
,
1431 make_unique
<fd_leak
> (*this, NULL_TREE
));
1435 fd_state_machine::check_for_dup (sm_context
*sm_ctxt
, const supernode
*node
,
1436 const gimple
*stmt
, const gcall
*call
,
1437 const tree callee_fndecl
, enum dup kind
) const
1439 tree lhs
= gimple_call_lhs (call
);
1440 tree arg_1
= gimple_call_arg (call
, 0);
1441 state_t state_arg_1
= sm_ctxt
->get_state (stmt
, arg_1
);
1442 if (state_arg_1
== m_stop
)
1444 if (!(is_constant_fd_p (state_arg_1
) || is_valid_fd_p (state_arg_1
)
1445 || state_arg_1
== m_start
))
1447 check_for_open_fd (sm_ctxt
, node
, stmt
, call
, callee_fndecl
,
1456 if (is_constant_fd_p (state_arg_1
) || state_arg_1
== m_start
)
1457 sm_ctxt
->set_next_state (stmt
, lhs
, m_unchecked_read_write
);
1459 sm_ctxt
->set_next_state (stmt
, lhs
,
1460 valid_to_unchecked_state (state_arg_1
));
1466 tree arg_2
= gimple_call_arg (call
, 1);
1467 state_t state_arg_2
= sm_ctxt
->get_state (stmt
, arg_2
);
1468 tree diag_arg_2
= sm_ctxt
->get_diagnostic_tree (arg_2
);
1469 if (state_arg_2
== m_stop
)
1471 /* Check if -1 was passed as second argument to dup2. */
1472 if (!(is_constant_fd_p (state_arg_2
) || is_valid_fd_p (state_arg_2
)
1473 || state_arg_2
== m_start
))
1477 make_unique
<fd_use_without_check
> (*this, diag_arg_2
,
1481 /* dup2 returns value of its second argument on success.But, the
1482 access mode of the returned file descriptor depends on the duplicated
1483 file descriptor i.e the first argument. */
1486 if (is_constant_fd_p (state_arg_1
) || state_arg_1
== m_start
)
1487 sm_ctxt
->set_next_state (stmt
, lhs
, m_unchecked_read_write
);
1489 sm_ctxt
->set_next_state (stmt
, lhs
,
1490 valid_to_unchecked_state (state_arg_1
));
1498 fd_state_machine::on_close (sm_context
*sm_ctxt
, const supernode
*node
,
1499 const gimple
*stmt
, const gcall
*call
) const
1501 tree arg
= gimple_call_arg (call
, 0);
1502 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1503 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1505 sm_ctxt
->on_transition (node
, stmt
, arg
, m_start
, m_closed
);
1506 sm_ctxt
->on_transition (node
, stmt
, arg
, m_unchecked_read_write
, m_closed
);
1507 sm_ctxt
->on_transition (node
, stmt
, arg
, m_unchecked_read_only
, m_closed
);
1508 sm_ctxt
->on_transition (node
, stmt
, arg
, m_unchecked_write_only
, m_closed
);
1509 sm_ctxt
->on_transition (node
, stmt
, arg
, m_valid_read_write
, m_closed
);
1510 sm_ctxt
->on_transition (node
, stmt
, arg
, m_valid_read_only
, m_closed
);
1511 sm_ctxt
->on_transition (node
, stmt
, arg
, m_valid_write_only
, m_closed
);
1512 sm_ctxt
->on_transition (node
, stmt
, arg
, m_constant_fd
, m_closed
);
1513 sm_ctxt
->on_transition (node
, stmt
, arg
, m_new_datagram_socket
, m_closed
);
1514 sm_ctxt
->on_transition (node
, stmt
, arg
, m_new_stream_socket
, m_closed
);
1515 sm_ctxt
->on_transition (node
, stmt
, arg
, m_new_unknown_socket
, m_closed
);
1516 sm_ctxt
->on_transition (node
, stmt
, arg
, m_bound_datagram_socket
, m_closed
);
1517 sm_ctxt
->on_transition (node
, stmt
, arg
, m_bound_stream_socket
, m_closed
);
1518 sm_ctxt
->on_transition (node
, stmt
, arg
, m_bound_unknown_socket
, m_closed
);
1519 sm_ctxt
->on_transition (node
, stmt
, arg
, m_listening_stream_socket
, m_closed
);
1520 sm_ctxt
->on_transition (node
, stmt
, arg
, m_connected_stream_socket
, m_closed
);
1522 if (is_closed_fd_p (state
))
1524 sm_ctxt
->warn (node
, stmt
, arg
,
1525 make_unique
<fd_double_close
> (*this, diag_arg
));
1526 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1530 fd_state_machine::on_read (sm_context
*sm_ctxt
, const supernode
*node
,
1531 const gimple
*stmt
, const gcall
*call
,
1532 const tree callee_fndecl
) const
1534 check_for_open_fd (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DIRS_READ
);
1537 fd_state_machine::on_write (sm_context
*sm_ctxt
, const supernode
*node
,
1538 const gimple
*stmt
, const gcall
*call
,
1539 const tree callee_fndecl
) const
1541 check_for_open_fd (sm_ctxt
, node
, stmt
, call
, callee_fndecl
, DIRS_WRITE
);
1545 fd_state_machine::check_for_open_fd (
1546 sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
1547 const gcall
*call
, const tree callee_fndecl
,
1548 enum access_directions callee_fndecl_dir
) const
1550 tree arg
= gimple_call_arg (call
, 0);
1551 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1552 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1554 if (is_closed_fd_p (state
))
1556 sm_ctxt
->warn (node
, stmt
, arg
,
1557 make_unique
<fd_use_after_close
> (*this, diag_arg
,
1563 if (state
== m_new_stream_socket
1564 || state
== m_bound_stream_socket
1565 || state
== m_listening_stream_socket
)
1566 /* Complain about fncall on socket in wrong phase. */
1569 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
1572 EXPECTED_PHASE_CAN_TRANSFER
));
1573 else if (!(is_valid_fd_p (state
)
1574 || state
== m_new_datagram_socket
1575 || state
== m_bound_unknown_socket
1576 || state
== m_connected_stream_socket
1578 || state
== m_stop
))
1580 if (!is_constant_fd_p (state
))
1583 make_unique
<fd_use_without_check
> (*this, diag_arg
,
1586 switch (callee_fndecl_dir
)
1588 case DIRS_READ_WRITE
:
1591 if (is_writeonly_fd_p (state
))
1593 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1594 sm_ctxt
->warn (node
, stmt
, arg
,
1595 make_unique
<fd_access_mode_mismatch
> (
1596 *this, diag_arg
, DIRS_WRITE
, callee_fndecl
));
1602 if (is_readonly_fd_p (state
))
1604 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1605 sm_ctxt
->warn (node
, stmt
, arg
,
1606 make_unique
<fd_access_mode_mismatch
> (
1607 *this, diag_arg
, DIRS_READ
, callee_fndecl
));
1615 add_constraint_ge_zero (region_model
*model
,
1616 const svalue
*fd_sval
,
1617 region_model_context
*ctxt
)
1620 = model
->get_manager ()->get_or_create_int_cst (integer_type_node
, 0);
1621 return model
->add_constraint (fd_sval
, GE_EXPR
, zero
, ctxt
);
1624 /* Get the state for a new socket type based on SOCKET_TYPE_SVAL,
1627 state_machine::state_t
1629 get_state_for_socket_type (const svalue
*socket_type_sval
) const
1631 if (tree socket_type_cst
= socket_type_sval
->maybe_get_constant ())
1633 /* Attempt to use SOCK_* constants stashed from the frontend. */
1634 if (tree_int_cst_equal (socket_type_cst
, m_SOCK_STREAM
))
1635 return m_new_stream_socket
;
1636 if (tree_int_cst_equal (socket_type_cst
, m_SOCK_DGRAM
))
1637 return m_new_datagram_socket
;
1640 /* Unrecognized constant, or a symbolic "type" value. */
1641 return m_new_unknown_socket
;
1644 /* Update the model and fd state for an outcome of a call to "socket",
1645 where SUCCESSFUL indicate which of the two outcomes.
1646 Return true if the outcome is feasible, or false to reject it. */
1649 fd_state_machine::on_socket (const call_details
&cd
,
1651 sm_context
*sm_ctxt
,
1652 const extrinsic_state
&ext_state
) const
1654 const gcall
*stmt
= cd
.get_call_stmt ();
1655 engine
*eng
= ext_state
.get_engine ();
1656 const supergraph
*sg
= eng
->get_supergraph ();
1657 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
1658 region_model
*model
= cd
.get_model ();
1662 if (gimple_call_lhs (stmt
))
1664 conjured_purge
p (model
, cd
.get_ctxt ());
1665 region_model_manager
*mgr
= model
->get_manager ();
1666 const svalue
*new_fd
1667 = mgr
->get_or_create_conjured_svalue (integer_type_node
,
1669 cd
.get_lhs_region (),
1671 if (!add_constraint_ge_zero (model
, new_fd
, cd
.get_ctxt ()))
1674 const svalue
*socket_type_sval
= cd
.get_arg_svalue (1);
1675 state_machine::state_t new_state
1676 = get_state_for_socket_type (socket_type_sval
);
1677 sm_ctxt
->on_transition (node
, stmt
, new_fd
, m_start
, new_state
);
1678 model
->set_value (cd
.get_lhs_region (), new_fd
, cd
.get_ctxt ());
1681 sm_ctxt
->warn (node
, stmt
, NULL_TREE
,
1682 make_unique
<fd_leak
> (*this, NULL_TREE
));
1686 /* Return -1; set errno. */
1687 model
->update_for_int_cst_return (cd
, -1, true);
1688 model
->set_errno (cd
);
1694 /* Check that FD_SVAL is usable by socket APIs.
1695 Complain if it has been closed, if it is a non-socket,
1697 If COMPLAINED is non-NULL and a problem is found,
1698 write *COMPLAINED = true.
1700 If SUCCESSFUL is true, attempt to add the constraint that FD_SVAL >= 0.
1701 Return true if this outcome is feasible. */
1704 fd_state_machine::check_for_socket_fd (const call_details
&cd
,
1706 sm_context
*sm_ctxt
,
1707 const svalue
*fd_sval
,
1708 const supernode
*node
,
1710 bool *complained
) const
1712 const gcall
*stmt
= cd
.get_call_stmt ();
1714 if (is_closed_fd_p (old_state
))
1716 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (fd_sval
);
1718 (node
, stmt
, fd_sval
,
1719 make_unique
<fd_use_after_close
> (*this, diag_arg
,
1720 cd
.get_fndecl_for_call ()));
1726 else if (is_unchecked_fd_p (old_state
) || is_valid_fd_p (old_state
))
1728 /* Complain about non-socket. */
1729 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (fd_sval
);
1731 (node
, stmt
, fd_sval
,
1732 make_unique
<fd_type_mismatch
> (*this, diag_arg
,
1733 cd
.get_fndecl_for_call (),
1735 EXPECTED_TYPE_SOCKET
));
1741 else if (old_state
== m_invalid
)
1743 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (fd_sval
);
1745 (node
, stmt
, fd_sval
,
1746 make_unique
<fd_use_without_check
> (*this, diag_arg
,
1747 cd
.get_fndecl_for_call ()));
1755 if (!add_constraint_ge_zero (cd
.get_model (), fd_sval
, cd
.get_ctxt ()))
1761 /* For use by "bind" and "connect".
1762 As per fd_state_machine::check_for_socket_fd above,
1763 but also complain if we don't have a new socket, and check that
1764 we can read up to the size bytes from the address. */
1767 fd_state_machine::check_for_new_socket_fd (const call_details
&cd
,
1769 sm_context
*sm_ctxt
,
1770 const svalue
*fd_sval
,
1771 const supernode
*node
,
1773 enum expected_phase expected_phase
)
1776 bool complained
= false;
1778 /* Check address and len. */
1779 const svalue
*address_sval
= cd
.get_arg_svalue (1);
1780 const svalue
*len_sval
= cd
.get_arg_svalue (2);
1782 /* Check that we can read the given number of bytes from the
1784 region_model
*model
= cd
.get_model ();
1785 const region
*address_reg
1786 = model
->deref_rvalue (address_sval
, cd
.get_arg_tree (1),
1788 const region
*sized_address_reg
1789 = model
->get_manager ()->get_sized_region (address_reg
,
1792 model
->get_store_value (sized_address_reg
, cd
.get_ctxt ());
1794 if (!check_for_socket_fd (cd
, successful
, sm_ctxt
,
1795 fd_sval
, node
, old_state
, &complained
))
1797 else if (!complained
1798 && !(old_state
== m_new_stream_socket
1799 || old_state
== m_new_datagram_socket
1800 || old_state
== m_new_unknown_socket
1801 || old_state
== m_start
1802 || old_state
== m_stop
1803 || old_state
== m_constant_fd
))
1805 /* Complain about "bind" or "connect" in wrong phase. */
1806 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (fd_sval
);
1808 (node
, cd
.get_call_stmt (), fd_sval
,
1809 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
1810 cd
.get_fndecl_for_call (),
1816 else if (!successful
)
1818 /* If we were in the start state, assume we had a new socket. */
1819 if (old_state
== m_start
)
1820 sm_ctxt
->set_next_state (cd
.get_call_stmt (), fd_sval
,
1821 m_new_unknown_socket
);
1824 /* Passing NULL as the address will lead to failure. */
1826 if (address_sval
->all_zeroes_p ())
1832 /* Update the model and fd state for an outcome of a call to "bind",
1833 where SUCCESSFUL indicate which of the two outcomes.
1834 Return true if the outcome is feasible, or false to reject it. */
1837 fd_state_machine::on_bind (const call_details
&cd
,
1839 sm_context
*sm_ctxt
,
1840 const extrinsic_state
&ext_state
) const
1842 const gcall
*stmt
= cd
.get_call_stmt ();
1843 engine
*eng
= ext_state
.get_engine ();
1844 const supergraph
*sg
= eng
->get_supergraph ();
1845 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
1846 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
1847 region_model
*model
= cd
.get_model ();
1848 state_t old_state
= sm_ctxt
->get_state (stmt
, fd_sval
);
1850 if (!check_for_new_socket_fd (cd
, successful
, sm_ctxt
,
1851 fd_sval
, node
, old_state
,
1852 EXPECTED_PHASE_CAN_BIND
))
1857 state_t next_state
= NULL
;
1858 if (old_state
== m_new_stream_socket
)
1859 next_state
= m_bound_stream_socket
;
1860 else if (old_state
== m_new_datagram_socket
)
1861 next_state
= m_bound_datagram_socket
;
1862 else if (old_state
== m_new_unknown_socket
)
1863 next_state
= m_bound_unknown_socket
;
1864 else if (old_state
== m_start
1865 || old_state
== m_constant_fd
)
1866 next_state
= m_bound_unknown_socket
;
1867 else if (old_state
== m_stop
)
1868 next_state
= m_stop
;
1871 sm_ctxt
->set_next_state (cd
.get_call_stmt (), fd_sval
, next_state
);
1872 model
->update_for_zero_return (cd
, true);
1876 /* Return -1; set errno. */
1877 model
->update_for_int_cst_return (cd
, -1, true);
1878 model
->set_errno (cd
);
1884 /* Update the model and fd state for an outcome of a call to "listen",
1885 where SUCCESSFUL indicate which of the two outcomes.
1886 Return true if the outcome is feasible, or false to reject it. */
1889 fd_state_machine::on_listen (const call_details
&cd
,
1891 sm_context
*sm_ctxt
,
1892 const extrinsic_state
&ext_state
) const
1894 const gcall
*stmt
= cd
.get_call_stmt ();
1895 engine
*eng
= ext_state
.get_engine ();
1896 const supergraph
*sg
= eng
->get_supergraph ();
1897 const supernode
*node
= sg
->get_supernode_for_stmt (cd
.get_call_stmt ());
1898 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
1899 region_model
*model
= cd
.get_model ();
1900 state_t old_state
= sm_ctxt
->get_state (stmt
, fd_sval
);
1902 /* We expect a stream socket that's had "bind" called on it. */
1903 if (!check_for_socket_fd (cd
, successful
, sm_ctxt
, fd_sval
, node
, old_state
))
1905 if (!(old_state
== m_start
1906 || old_state
== m_constant_fd
1907 || old_state
== m_stop
1908 || old_state
== m_invalid
1909 || old_state
== m_bound_stream_socket
1910 || old_state
== m_bound_unknown_socket
1911 /* Assume it's OK to call "listen" more than once. */
1912 || old_state
== m_listening_stream_socket
))
1914 /* Complain about fncall on wrong type or in wrong phase. */
1915 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (fd_sval
);
1916 if (is_stream_socket_fd_p (old_state
))
1918 (node
, stmt
, fd_sval
,
1919 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
1920 cd
.get_fndecl_for_call (),
1922 EXPECTED_PHASE_CAN_LISTEN
));
1925 (node
, stmt
, fd_sval
,
1926 make_unique
<fd_type_mismatch
> (*this, diag_arg
,
1927 cd
.get_fndecl_for_call (),
1929 EXPECTED_TYPE_STREAM_SOCKET
));
1936 model
->update_for_zero_return (cd
, true);
1937 sm_ctxt
->set_next_state (cd
.get_call_stmt (), fd_sval
,
1938 m_listening_stream_socket
);
1942 /* Return -1; set errno. */
1943 model
->update_for_int_cst_return (cd
, -1, true);
1944 model
->set_errno (cd
);
1945 if (old_state
== m_start
)
1946 sm_ctxt
->set_next_state (cd
.get_call_stmt (), fd_sval
,
1947 m_bound_stream_socket
);
1953 /* Update the model and fd state for an outcome of a call to "accept",
1954 where SUCCESSFUL indicate which of the two outcomes.
1955 Return true if the outcome is feasible, or false to reject it. */
1958 fd_state_machine::on_accept (const call_details
&cd
,
1960 sm_context
*sm_ctxt
,
1961 const extrinsic_state
&ext_state
) const
1963 const gcall
*stmt
= cd
.get_call_stmt ();
1964 engine
*eng
= ext_state
.get_engine ();
1965 const supergraph
*sg
= eng
->get_supergraph ();
1966 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
1967 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
1968 const svalue
*address_sval
= cd
.get_arg_svalue (1);
1969 const svalue
*len_ptr_sval
= cd
.get_arg_svalue (2);
1970 region_model
*model
= cd
.get_model ();
1971 state_t old_state
= sm_ctxt
->get_state (stmt
, fd_sval
);
1973 if (!address_sval
->all_zeroes_p ())
1975 region_model_manager
*mgr
= model
->get_manager ();
1977 /* We might have a union of various pointer types, rather than a
1978 pointer type; cast to (void *) before dereferencing. */
1979 address_sval
= mgr
->get_or_create_cast (ptr_type_node
, address_sval
);
1981 const region
*address_reg
1982 = model
->deref_rvalue (address_sval
, cd
.get_arg_tree (1),
1984 const region
*len_reg
1985 = model
->deref_rvalue (len_ptr_sval
, cd
.get_arg_tree (2),
1987 const svalue
*old_len_sval
1988 = model
->get_store_value (len_reg
, cd
.get_ctxt ());
1989 tree len_ptr
= cd
.get_arg_tree (2);
1990 tree star_len_ptr
= build2 (MEM_REF
, TREE_TYPE (TREE_TYPE (len_ptr
)),
1992 build_int_cst (TREE_TYPE (len_ptr
), 0));
1993 old_len_sval
= model
->check_for_poison (old_len_sval
,
1999 conjured_purge
p (model
, cd
.get_ctxt ());
2000 const region
*old_sized_address_reg
2001 = mgr
->get_sized_region (address_reg
,
2004 const svalue
*new_addr_sval
2005 = mgr
->get_or_create_conjured_svalue (NULL_TREE
,
2007 old_sized_address_reg
,
2009 model
->set_value (old_sized_address_reg
, new_addr_sval
,
2011 const svalue
*new_addr_len
2012 = mgr
->get_or_create_conjured_svalue (NULL_TREE
,
2016 model
->set_value (len_reg
, new_addr_len
, cd
.get_ctxt ());
2020 /* We expect a stream socket in the "listening" state. */
2021 if (!check_for_socket_fd (cd
, successful
, sm_ctxt
, fd_sval
, node
, old_state
))
2024 if (old_state
== m_start
|| old_state
== m_constant_fd
)
2025 /* If we were in the start state (or a constant), assume we had the
2027 sm_ctxt
->set_next_state (cd
.get_call_stmt (), fd_sval
,
2028 m_listening_stream_socket
);
2029 else if (old_state
== m_stop
)
2031 /* No further complaints. */
2033 else if (old_state
!= m_listening_stream_socket
)
2035 /* Complain about fncall on wrong type or in wrong phase. */
2036 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (fd_sval
);
2037 if (is_stream_socket_fd_p (old_state
))
2039 (node
, stmt
, fd_sval
,
2040 make_unique
<fd_phase_mismatch
> (*this, diag_arg
,
2041 cd
.get_fndecl_for_call (),
2043 EXPECTED_PHASE_CAN_ACCEPT
));
2046 (node
, stmt
, fd_sval
,
2047 make_unique
<fd_type_mismatch
> (*this, diag_arg
,
2048 cd
.get_fndecl_for_call (),
2050 EXPECTED_TYPE_STREAM_SOCKET
));
2057 /* Return new conjured FD in "connected" state. */
2058 if (gimple_call_lhs (stmt
))
2060 conjured_purge
p (model
, cd
.get_ctxt ());
2061 region_model_manager
*mgr
= model
->get_manager ();
2062 const svalue
*new_fd
2063 = mgr
->get_or_create_conjured_svalue (integer_type_node
,
2065 cd
.get_lhs_region (),
2067 if (!add_constraint_ge_zero (model
, new_fd
, cd
.get_ctxt ()))
2069 sm_ctxt
->on_transition (node
, stmt
, new_fd
,
2070 m_start
, m_connected_stream_socket
);
2071 model
->set_value (cd
.get_lhs_region (), new_fd
, cd
.get_ctxt ());
2074 sm_ctxt
->warn (node
, stmt
, NULL_TREE
,
2075 make_unique
<fd_leak
> (*this, NULL_TREE
));
2079 /* Return -1; set errno. */
2080 model
->update_for_int_cst_return (cd
, -1, true);
2081 model
->set_errno (cd
);
2087 /* Update the model and fd state for an outcome of a call to "connect",
2088 where SUCCESSFUL indicate which of the two outcomes.
2089 Return true if the outcome is feasible, or false to reject it. */
2092 fd_state_machine::on_connect (const call_details
&cd
,
2094 sm_context
*sm_ctxt
,
2095 const extrinsic_state
&ext_state
) const
2097 const gcall
*stmt
= cd
.get_call_stmt ();
2098 engine
*eng
= ext_state
.get_engine ();
2099 const supergraph
*sg
= eng
->get_supergraph ();
2100 const supernode
*node
= sg
->get_supernode_for_stmt (stmt
);
2101 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
2102 region_model
*model
= cd
.get_model ();
2103 state_t old_state
= sm_ctxt
->get_state (stmt
, fd_sval
);
2105 if (!check_for_new_socket_fd (cd
, successful
, sm_ctxt
,
2106 fd_sval
, node
, old_state
,
2107 EXPECTED_PHASE_CAN_CONNECT
))
2112 model
->update_for_zero_return (cd
, true);
2113 state_t next_state
= NULL
;
2114 if (old_state
== m_new_stream_socket
)
2115 next_state
= m_connected_stream_socket
;
2116 else if (old_state
== m_new_datagram_socket
)
2117 /* It's legal to call connect on a datagram socket, potentially
2118 more than once. We don't transition states for this. */
2119 next_state
= m_new_datagram_socket
;
2120 else if (old_state
== m_new_unknown_socket
)
2121 next_state
= m_stop
;
2122 else if (old_state
== m_start
2123 || old_state
== m_constant_fd
)
2124 next_state
= m_stop
;
2125 else if (old_state
== m_stop
)
2126 next_state
= m_stop
;
2129 sm_ctxt
->set_next_state (cd
.get_call_stmt (), fd_sval
, next_state
);
2133 /* Return -1; set errno. */
2134 model
->update_for_int_cst_return (cd
, -1, true);
2135 model
->set_errno (cd
);
2136 /* TODO: perhaps transition to a failed state, since the
2137 portable way to handle a failed "connect" is to close
2138 the socket and try again with a new socket. */
2145 fd_state_machine::on_condition (sm_context
*sm_ctxt
, const supernode
*node
,
2146 const gimple
*stmt
, const svalue
*lhs
,
2147 enum tree_code op
, const svalue
*rhs
) const
2149 if (tree cst
= rhs
->maybe_get_constant ())
2151 if (TREE_CODE (cst
) == INTEGER_CST
)
2153 int val
= TREE_INT_CST_LOW (cst
);
2157 make_valid_transitions_on_condition (sm_ctxt
, node
, stmt
, lhs
);
2159 else if (op
== EQ_EXPR
)
2160 make_invalid_transitions_on_condition (sm_ctxt
, node
, stmt
,
2166 if (rhs
->all_zeroes_p ())
2169 make_valid_transitions_on_condition (sm_ctxt
, node
, stmt
, lhs
);
2170 else if (op
== LT_EXPR
)
2171 make_invalid_transitions_on_condition (sm_ctxt
, node
, stmt
, lhs
);
2176 fd_state_machine::make_valid_transitions_on_condition (sm_context
*sm_ctxt
,
2177 const supernode
*node
,
2179 const svalue
*lhs
) const
2181 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_read_write
,
2182 m_valid_read_write
);
2183 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_read_only
,
2185 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_write_only
,
2186 m_valid_write_only
);
2190 fd_state_machine::make_invalid_transitions_on_condition (
2191 sm_context
*sm_ctxt
, const supernode
*node
, const gimple
*stmt
,
2192 const svalue
*lhs
) const
2194 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_read_write
, m_invalid
);
2195 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_read_only
, m_invalid
);
2196 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_unchecked_write_only
, m_invalid
);
2200 fd_state_machine::can_purge_p (state_t s
) const
2202 if (is_unchecked_fd_p (s
)
2203 || is_valid_fd_p (s
)
2204 || is_socket_fd_p (s
))
2210 std::unique_ptr
<pending_diagnostic
>
2211 fd_state_machine::on_leak (tree var
) const
2213 return make_unique
<fd_leak
> (*this, var
);
2218 make_fd_state_machine (logger
*logger
)
2220 return new fd_state_machine (logger
);
2224 get_fd_state (region_model_context
*ctxt
,
2225 sm_state_map
**out_smap
,
2226 const fd_state_machine
**out_sm
,
2227 unsigned *out_sm_idx
,
2228 std::unique_ptr
<sm_context
> *out_sm_context
)
2233 const state_machine
*sm
;
2234 if (!ctxt
->get_fd_map (out_smap
, &sm
, out_sm_idx
, out_sm_context
))
2239 *out_sm
= (const fd_state_machine
*)sm
;
2243 /* Specialcase hook for handling pipe, for use by
2244 kf_pipe::success::update_model. */
2247 region_model::mark_as_valid_fd (const svalue
*sval
, region_model_context
*ctxt
)
2250 const fd_state_machine
*fd_sm
;
2251 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, NULL
))
2253 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2256 fd_sm
->mark_as_valid_fd (this, smap
, sval
, *ext_state
);
2259 /* Handle calls to "socket".
2260 See e.g. https://man7.org/linux/man-pages/man3/socket.3p.html */
2262 class kf_socket
: public known_function
2265 class outcome_of_socket
: public succeed_or_fail_call_info
2268 outcome_of_socket (const call_details
&cd
, bool success
)
2269 : succeed_or_fail_call_info (cd
, success
)
2272 bool update_model (region_model
*model
,
2273 const exploded_edge
*,
2274 region_model_context
*ctxt
) const final override
2276 const call_details
cd (get_call_details (model
, ctxt
));
2278 const fd_state_machine
*fd_sm
;
2279 std::unique_ptr
<sm_context
> sm_ctxt
;
2280 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2282 cd
.set_any_lhs_with_defaults ();
2285 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2288 cd
.set_any_lhs_with_defaults ();
2292 return fd_sm
->on_socket (cd
, m_success
, sm_ctxt
.get (), *ext_state
);
2296 bool matches_call_types_p (const call_details
&cd
) const final override
2298 return cd
.num_args () == 3;
2301 void impl_call_post (const call_details
&cd
) const final override
2305 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_socket
> (cd
, false));
2306 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_socket
> (cd
, true));
2307 cd
.get_ctxt ()->terminate_path ();
2312 /* Handle calls to "bind".
2313 See e.g. https://man7.org/linux/man-pages/man3/bind.3p.html */
2315 class kf_bind
: public known_function
2318 class outcome_of_bind
: public succeed_or_fail_call_info
2321 outcome_of_bind (const call_details
&cd
, bool success
)
2322 : succeed_or_fail_call_info (cd
, success
)
2325 bool update_model (region_model
*model
,
2326 const exploded_edge
*,
2327 region_model_context
*ctxt
) const final override
2329 const call_details
cd (get_call_details (model
, ctxt
));
2331 const fd_state_machine
*fd_sm
;
2332 std::unique_ptr
<sm_context
> sm_ctxt
;
2333 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2335 cd
.set_any_lhs_with_defaults ();
2338 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2341 cd
.set_any_lhs_with_defaults ();
2344 return fd_sm
->on_bind (cd
, m_success
, sm_ctxt
.get (), *ext_state
);
2348 bool matches_call_types_p (const call_details
&cd
) const final override
2350 return (cd
.num_args () == 3 && cd
.arg_is_pointer_p (1));
2353 void impl_call_post (const call_details
&cd
) const final override
2357 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_bind
> (cd
, false));
2358 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_bind
> (cd
, true));
2359 cd
.get_ctxt ()->terminate_path ();
2364 /* Handle calls to "listen".
2365 See e.g. https://man7.org/linux/man-pages/man3/listen.3p.html */
2367 class kf_listen
: public known_function
2369 class outcome_of_listen
: public succeed_or_fail_call_info
2372 outcome_of_listen (const call_details
&cd
, bool success
)
2373 : succeed_or_fail_call_info (cd
, success
)
2376 bool update_model (region_model
*model
,
2377 const exploded_edge
*,
2378 region_model_context
*ctxt
) const final override
2380 const call_details
cd (get_call_details (model
, ctxt
));
2382 const fd_state_machine
*fd_sm
;
2383 std::unique_ptr
<sm_context
> sm_ctxt
;
2384 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2386 cd
.set_any_lhs_with_defaults ();
2389 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2392 cd
.set_any_lhs_with_defaults ();
2396 return fd_sm
->on_listen (cd
, m_success
, sm_ctxt
.get (), *ext_state
);
2400 bool matches_call_types_p (const call_details
&cd
) const final override
2402 return cd
.num_args () == 2;
2405 void impl_call_post (const call_details
&cd
) const final override
2409 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_listen
> (cd
, false));
2410 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_listen
> (cd
, true));
2411 cd
.get_ctxt ()->terminate_path ();
2416 /* Handle calls to "accept".
2417 See e.g. https://man7.org/linux/man-pages/man3/accept.3p.html */
2419 class kf_accept
: public known_function
2421 class outcome_of_accept
: public succeed_or_fail_call_info
2424 outcome_of_accept (const call_details
&cd
, bool success
)
2425 : succeed_or_fail_call_info (cd
, success
)
2428 bool update_model (region_model
*model
,
2429 const exploded_edge
*,
2430 region_model_context
*ctxt
) const final override
2432 const call_details
cd (get_call_details (model
, ctxt
));
2434 const fd_state_machine
*fd_sm
;
2435 std::unique_ptr
<sm_context
> sm_ctxt
;
2436 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2438 cd
.set_any_lhs_with_defaults ();
2441 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2444 cd
.set_any_lhs_with_defaults ();
2448 return fd_sm
->on_accept (cd
, m_success
, sm_ctxt
.get (), *ext_state
);
2452 bool matches_call_types_p (const call_details
&cd
) const final override
2454 return (cd
.num_args () == 3
2455 && cd
.arg_is_pointer_p (1)
2456 && cd
.arg_is_pointer_p (2));
2459 void impl_call_post (const call_details
&cd
) const final override
2463 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_accept
> (cd
, false));
2464 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_accept
> (cd
, true));
2465 cd
.get_ctxt ()->terminate_path ();
2470 /* Handle calls to "connect".
2471 See e.g. https://man7.org/linux/man-pages/man3/connect.3p.html */
2473 class kf_connect
: public known_function
2476 class outcome_of_connect
: public succeed_or_fail_call_info
2479 outcome_of_connect (const call_details
&cd
, bool success
)
2480 : succeed_or_fail_call_info (cd
, success
)
2483 bool update_model (region_model
*model
,
2484 const exploded_edge
*,
2485 region_model_context
*ctxt
) const final override
2487 const call_details
cd (get_call_details (model
, ctxt
));
2489 const fd_state_machine
*fd_sm
;
2490 std::unique_ptr
<sm_context
> sm_ctxt
;
2491 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2493 cd
.set_any_lhs_with_defaults ();
2496 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2499 cd
.set_any_lhs_with_defaults ();
2503 return fd_sm
->on_connect (cd
, m_success
, sm_ctxt
.get (), *ext_state
);
2507 bool matches_call_types_p (const call_details
&cd
) const final override
2509 return (cd
.num_args () == 3
2510 && cd
.arg_is_pointer_p (1));
2513 void impl_call_post (const call_details
&cd
) const final override
2517 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_connect
> (cd
, false));
2518 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_connect
> (cd
, true));
2519 cd
.get_ctxt ()->terminate_path ();
2524 /* Handler for "isatty"".
2525 See e.g. https://man7.org/linux/man-pages/man3/isatty.3.html */
2527 class kf_isatty
: public known_function
2529 class outcome_of_isatty
: public succeed_or_fail_call_info
2532 outcome_of_isatty (const call_details
&cd
, bool success
)
2533 : succeed_or_fail_call_info (cd
, success
)
2536 bool update_model (region_model
*model
,
2537 const exploded_edge
*,
2538 region_model_context
*ctxt
) const final override
2540 const call_details
cd (get_call_details (model
, ctxt
));
2545 model
->update_for_int_cst_return (cd
, 1, true);
2549 /* Return 0; set errno. */
2550 model
->update_for_int_cst_return (cd
, 0, true);
2551 model
->set_errno (cd
);
2554 return feasible_p (cd
, ctxt
);
2558 bool feasible_p (const call_details
&cd
,
2559 region_model_context
*ctxt
) const
2563 /* Can't be "success" on a closed/invalid fd. */
2565 const fd_state_machine
*fd_sm
;
2566 std::unique_ptr
<sm_context
> sm_ctxt
;
2567 if (!get_fd_state (ctxt
, &smap
, &fd_sm
, NULL
, &sm_ctxt
))
2569 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2573 const svalue
*fd_sval
= cd
.get_arg_svalue (0);
2574 state_machine::state_t old_state
2575 = sm_ctxt
->get_state (cd
.get_call_stmt (), fd_sval
);
2577 if (fd_sm
->is_closed_fd_p (old_state
)
2578 || old_state
== fd_sm
->m_invalid
)
2583 }; // class outcome_of_isatty
2586 bool matches_call_types_p (const call_details
&cd
) const final override
2588 return cd
.num_args () == 1;
2591 void impl_call_post (const call_details
&cd
) const final override
2595 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_isatty
> (cd
, false));
2596 cd
.get_ctxt ()->bifurcate (make_unique
<outcome_of_isatty
> (cd
, true));
2597 cd
.get_ctxt ()->terminate_path ();
2602 /* Handler for calls to "pipe" and "pipe2".
2603 See e.g. https://www.man7.org/linux/man-pages/man2/pipe.2.html */
2605 class kf_pipe
: public known_function
2607 class failure
: public failed_call_info
2610 failure (const call_details
&cd
) : failed_call_info (cd
) {}
2612 bool update_model (region_model
*model
,
2613 const exploded_edge
*,
2614 region_model_context
*ctxt
) const final override
2616 /* Return -1; everything else is unchanged. */
2617 const call_details
cd (get_call_details (model
, ctxt
));
2618 model
->update_for_int_cst_return (cd
, -1, true);
2623 class success
: public success_call_info
2626 success (const call_details
&cd
) : success_call_info (cd
) {}
2628 bool update_model (region_model
*model
,
2629 const exploded_edge
*,
2630 region_model_context
*ctxt
) const final override
2632 const call_details
cd (get_call_details (model
, ctxt
));
2635 model
->update_for_zero_return (cd
, true);
2637 /* Update fd array. */
2638 region_model_manager
*mgr
= cd
.get_manager ();
2639 tree arr_tree
= cd
.get_arg_tree (0);
2640 const svalue
*arr_sval
= cd
.get_arg_svalue (0);
2641 for (int idx
= 0; idx
< 2; idx
++)
2643 const region
*arr_reg
2644 = model
->deref_rvalue (arr_sval
, arr_tree
, cd
.get_ctxt ());
2645 const svalue
*idx_sval
2646 = mgr
->get_or_create_int_cst (integer_type_node
, idx
);
2647 const region
*element_reg
2648 = mgr
->get_element_region (arr_reg
, integer_type_node
, idx_sval
);
2649 conjured_purge
p (model
, cd
.get_ctxt ());
2650 const svalue
*fd_sval
2651 = mgr
->get_or_create_conjured_svalue (integer_type_node
,
2652 cd
.get_call_stmt (),
2655 model
->set_value (element_reg
, fd_sval
, cd
.get_ctxt ());
2656 model
->mark_as_valid_fd (fd_sval
, cd
.get_ctxt ());
2663 kf_pipe (unsigned num_args
)
2664 : m_num_args (num_args
)
2666 gcc_assert (num_args
> 0);
2669 bool matches_call_types_p (const call_details
&cd
) const final override
2671 return (cd
.num_args () == m_num_args
&& cd
.arg_is_pointer_p (0));
2674 void impl_call_post (const call_details
&cd
) const final override
2678 cd
.get_ctxt ()->bifurcate (make_unique
<failure
> (cd
));
2679 cd
.get_ctxt ()->bifurcate (make_unique
<success
> (cd
));
2680 cd
.get_ctxt ()->terminate_path ();
2685 unsigned m_num_args
;
2688 /* Handler for "read".
2689 ssize_t read(int fildes, void *buf, size_t nbyte);
2690 See e.g. https://man7.org/linux/man-pages/man2/read.2.html */
2692 class kf_read
: public known_function
2695 bool matches_call_types_p (const call_details
&cd
) const final override
2697 return (cd
.num_args () == 3
2698 && cd
.arg_is_pointer_p (1)
2699 && cd
.arg_is_size_p (2));
2702 /* For now, assume that any call to "read" fully clobbers the buffer
2703 passed in. This isn't quite correct (e.g. errors, partial reads;
2704 see PR analyzer/108689), but at least stops us falsely complaining
2705 about the buffer being uninitialized. */
2706 void impl_call_pre (const call_details
&cd
) const final override
2708 region_model
*model
= cd
.get_model ();
2709 const svalue
*ptr_sval
= cd
.get_arg_svalue (1);
2710 if (const region
*reg
= ptr_sval
->maybe_get_region ())
2712 const region
*base_reg
= reg
->get_base_region ();
2713 const svalue
*new_sval
= cd
.get_or_create_conjured_svalue (base_reg
);
2714 model
->set_value (base_reg
, new_sval
, cd
.get_ctxt ());
2716 cd
.set_any_lhs_with_defaults ();
2721 /* Populate KFM with instances of known functions relating to
2722 file descriptors. */
2725 register_known_fd_functions (known_function_manager
&kfm
)
2727 kfm
.add ("accept", make_unique
<kf_accept
> ());
2728 kfm
.add ("bind", make_unique
<kf_bind
> ());
2729 kfm
.add ("connect", make_unique
<kf_connect
> ());
2730 kfm
.add ("isatty", make_unique
<kf_isatty
> ());
2731 kfm
.add ("listen", make_unique
<kf_listen
> ());
2732 kfm
.add ("pipe", make_unique
<kf_pipe
> (1));
2733 kfm
.add ("pipe2", make_unique
<kf_pipe
> (2));
2734 kfm
.add ("read", make_unique
<kf_read
> ());
2735 kfm
.add ("socket", make_unique
<kf_socket
> ());
2740 #endif // ENABLE_ANALYZER