blob: da925b0989fefb258e50d324c60e28f704343d5d [file] [log] [blame]
/* An overview of the state machine from sm-fd.cc.
Copyright (C) 2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* Keep this in-sync with sm-fd.cc */
digraph "fd" {
/* STATES. */
/* Start state. */
start;
/* States representing a file descriptor that hasn't yet been
checked for validity after opening, for three different
access modes. */
unchecked_read_write;
unchecked_read_only;
unchecked_write_only;
/* States for representing a file descriptor that is known to be valid (>=
0), for three different access modes. */
valid_read_write;
valid_read_only;
valid_write_only;
/* State for a file descriptor that is known to be invalid (< 0). */
invalid;
/* State for a file descriptor that has been closed. */
closed;
/* States for FDs relating to socket APIs. */
/* Result of successful "socket" with SOCK_DGRAM. */
new_datagram_socket;
/* Result of successful "socket" with SOCK_STREAM. */
new_stream_socket;
/* Result of successful "socket" with unknown type. */
new_unknown_socket;
/* The above after a successful call to "bind". */
bound_datagram_socket;
bound_stream_socket;
bound_unknown_socket;
/* A bound socket after a successful call to "listen" (stream or unknown). */
listening_stream_socket;
/* (i) the new FD as a result of a succesful call to "accept" on a
listening socket (via a passive open), or
(ii) an active socket after a successful call to "connect"
(via an active open). */
connected_stream_socket;
/* State for a file descriptor that we do not want to track anymore . */
stop;
/* TRANSITIONS. */
/* On "open". */
start -> unchecked_read_only [label="on 'X = open(..., O_RDONLY);'"];
start -> unchecked_write_only [label="on 'X = open(..., O_WRONLY);'"];
start -> unchecked_read_write [label="on 'X = open(..., ...);'"];
/* On "creat". */
start -> unchecked_write_only [label="on 'X = create(...);'"];
/* On "close". */
start -> closed [label="on 'close(X);'"];
unchecked_read_write -> closed [label="on 'close(X);'"];
unchecked_read_only -> closed [label="on 'close(X);'"];
unchecked_write_only -> closed [label="on 'close(X);'"];
valid_read_write -> closed [label="on 'close(X);'"];
valid_read_only -> closed [label="on 'close(X);'"];
valid_write_only -> closed [label="on 'close(X);'"];
constant_fd -> closed [label="on 'close(X);'"];
new_datagram_socket -> closed [label="on 'close(X);'"];
new_stream_socket -> closed [label="on 'close(X);'"];
new_unknown_socket -> closed [label="on 'close(X);'"];
bound_datagram_socket -> closed [label="on 'close(X);'"];
bound_stream_socket -> closed [label="on 'close(X);'"];
bound_unknown_socket -> closed [label="on 'close(X);'"];
listening_stream_socket -> closed [label="on 'close(X);'"];
connected_stream_socket -> closed [label="on 'close(X);'"];
closed -> stop [label="on 'close(X);':\nWarn('double close')"];
/* On "read". */
closed -> closed [label="on 'read(X);':\nWarn('use after close')"];
unchecked_read_write -> unchecked_read_write [label="on 'read(X);:\nWarn('use without check')'"];
unchecked_read_only -> unchecked_read_only [label="on 'read(X);:\nWarn('use without check')'"];
unchecked_write_only -> unchecked_write_only [label="on 'read(X);:\nWarn('use without check')'"];
valid_write_only -> valid_write_only [label="on 'read(X);:\nWarn('access mode mismatch')'"];
/* On "write". */
closed -> closed [label="on 'write(X);':\nWarn('use after close')"];
unchecked_read_write -> unchecked_read_write [label="on 'write(X);:\nWarn('use without check')'"];
unchecked_read_only -> unchecked_read_only [label="on 'write(X);:\nWarn('use without check')'"];
unchecked_write_only -> unchecked_write_only [label="on 'write(X);:\nWarn('use without check')'"];
valid_read_only -> valid_read_only [label="on 'write(X);:\nWarn('access mode mismatch')'"];
/* On "dup". */
closed -> closed [label="on 'dup(X);':\nWarn('use after close')"];
/* plus stuff for the new fd. */
/* On "pipe". */
start -> valid_read_write [label="when 'pipe()' succeeds"];
/* On "socket". */
start -> new_datagram_socket [label="when 'socket(..., SOCK_DGRAM, ...)' succeeds"];
start -> new_stream_socket [label="when 'socket(..., SOCK_STREAM, ...)' succeeds"];
start -> new_unknown_socket [label="when 'socket(..., ..., ...)' succeeds"];
/* On "bind". */
start -> bound_unknown_socket [label="when 'bind(X, ...)' succeeds"];
new_stream_socket -> bound_stream_socket [label="when 'bind(X, ...)' succeeds"];
new_datagram_socket -> bound_datagram_socket [label="when 'bind(X, ...)' succeeds"];
new_unknown_socket -> bound_unknown_socket [label="when 'bind(X, ...)' succeeds"];
/* On "listen". */
start -> listening_stream_socket [label="when 'listen(X, ...)' succeeds"];
bound_stream_socket -> listening_stream_socket [label="when 'listen(X, ...)' succeeds"];
bound_unknown_socket -> listening_stream_socket [label="when 'listen(X, ...)' succeeds"];
/* On "accept". */
start -> connected_stream_socket [label="when 'accept(OTHER, ...)' succeeds on a listening_stream_socket"];
/* On "connect". */
new_stream_socket -> connected_stream_socket [label="when 'connect(X, ...)' succeeds"];
new_datagram_socket -> new_datagram_socket [label="when 'connect(X, ...)' succeeds"];
new_unknown_socket -> stop [label="when 'connect(X, ...)' succeeds"];
start -> stop [label="when 'connect(X, ...)' succeeds"];
/* on_condition. */
unchecked_read_write -> valid_read_write [label="on 'X >= 0'"];
unchecked_read_only -> valid_read_only [label="on 'X >= 0'"];
unchecked_write_only -> valid_write_only [label="on 'X >= 0'"];
unchecked_read_write -> invalid [label="on 'X < 0'"];
unchecked_read_only -> invalid [label="on 'X < 0'"];
unchecked_write_only -> invalid [label="on 'X < 0'"];
/* Leaks. */
unchecked_read_write -> stop [label="on leak:\nWarn('leak')"];
unchecked_read_only -> stop [label="on leak:\nWarn('leak')"];
unchecked_write_only -> stop [label="on leak:\nWarn('leak')"];
valid_read_write -> stop [label="on leak:\nWarn('leak')"];
valid_read_only -> stop [label="on leak:\nWarn('leak')"];
valid_write_only -> stop [label="on leak:\nWarn('leak')"];
new_datagram_socket -> stop [label="on leak:\nWarn('leak')"];
new_stream_socket -> stop [label="on leak:\nWarn('leak')"];
new_unknown_socket -> stop [label="on leak:\nWarn('leak')"];
bound_datagram_socket -> stop [label="on leak:\nWarn('leak')"];
bound_stream_socket -> stop [label="on leak:\nWarn('leak')"];
bound_unknown_socket -> stop [label="on leak:\nWarn('leak')"];
listening_stream_socket -> stop [label="on leak:\nWarn('leak')"];
connected_stream_socket -> stop [label="on leak:\nWarn('leak')"];
}