/* An overview of the state machine from sm-fd.cc. Copyright (C) 2022-2024 Free Software Foundation, Inc. Contributed by David Malcolm . 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 . */ /* Keep this in-sync with sm-fd.cc */ digraph "fd" { /* STATES. */ /* Start state. */ start; /* State for a constant file descriptor (>= 0). */ constant_fd; /* 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"]; constant_fd -> 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"]; constant_fd -> 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"]; constant_fd -> 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')"]; }