| /* 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')"]; |
| } |