casaroli opened a new pull request, #21412:
URL: https://github.com/apache/nuttx/pull/21412

   ## Summary
   
   POSIX `send()`: a send on a connection-mode socket that is no longer 
connected fails with `EPIPE`, and the caller gets `SIGPIPE` unless 
`MSG_NOSIGNAL` is given. After the peer closed a TCP connection, the first send 
gets a reset, and every send after it failed with `ENOTCONN` in NuttX. So 
`psock_sigpipe()` raised no `SIGPIPE`, and a program that writes to a closed 
connection did not end.
   
   The TCP send paths (buffered send, unbuffered send and `tcp_sendfile()`, and 
the lost-connection branch of the event handlers of the last two) now return 
`-EPIPE` when the connection was closed by the peer (`_SF_CLOSED` set), and 
`-ENOTCONN` as before for a socket that never connected. A connection lost by a 
timeout or a network down event clears `_SF_CLOSED`, so it still gives 
`ENOTCONN`. `recv()` and `poll()` do not change: after the reset `recv()` 
returns 0 and `poll()` reports `POLLHUP`.
   
   The PR has five commits:
   
   - `net/tcp/tcp_sendfile.c`: a whitespace nxstyle fix, so that CI can check 
the whole file.
   - `fs/nfs`: `nfs_request()` connects to the server again when a request 
fails with `-ENOTCONN` (for example after the server closed an idle TCP 
connection). It now does this for `-EPIPE` too, so the reconnect continues to 
work with the next commit.
   - `net/tcp`: the `EPIPE` change.
   - `fs/vfs`: `sendfile()` to a socket through `psock_sendfile()` now raises 
`SIGPIPE` on `EPIPE`, as its slow path through `write()` already does.
   - `Documentation`: the `EPIPE` entry of `sendto()` in `11_network.rst` names 
the closed connection too.
   
   ## Impact
   
   All build modes with `CONFIG_NET_TCP` and the NuttX TCP stack. usrsock 
sockets do not change (they return what the usrsock daemon gives).
   
   This changes the behaviour of the flat build too: a send, `write()` or 
`sendfile()` on a TCP socket whose peer closed the connection now fails with 
`EPIPE` instead of `ENOTCONN`. A program that checks for `ENOTCONN` after a 
send must also check for `EPIPE`. In the tree, only `fs/nfs` did this, and this 
PR changes it.
   
   With `EPIPE`, `send()`, `sendto()`, `sendmsg()`, `write()` and now 
`sendfile()` raise `SIGPIPE` (unless `MSG_NOSIGNAL`). A program with a handler: 
the handler now runs. A program that ignores `SIGPIPE`: no change except the 
errno. A program without a handler: with `CONFIG_SIG_SIGPIPE_ACTION=y` it now 
ends, as the POSIX default action says; without that option (the default, no 
in-tree defconfig sets it) the signal is discarded and only the errno changes. 
The `psock_send*()` interfaces that the OS uses (telnet driver, NFS, 9P, VNC, 
OP-TEE, usrsock server) raise no signal; they only see the new errno.
   
   In-tree programs that write to a TCP connection without `MSG_NOSIGNAL` and 
without ignoring `SIGPIPE` end with `CONFIG_SIG_SIGPIPE_ACTION=y` when they 
write after the peer closed: for example `netutils/webclient` (wget), 
`netutils/ftpc`, `netutils/netcat`, `netutils/iperf` (TCP client), 
`netutils/smtp`, `system/telnet`, `system/gdbstub`. The servers in nuttx-apps 
(ftpd, webserver, thttpd, telnet chatd, fastboot, nxmodbus, dropbear, rexecd 
and the examples) already use `MSG_NOSIGNAL` or ignore `SIGPIPE`.
   
   ## Testing
   
   Base: master ed6f81a1be7 (all five configurations ran on ae2f4f9d87f, the 
first again on ed6f81a1be7; no change in `net/` or `fs/` between them). Test 
program `tcpepipe.c` (one TCP connection over loopback per case; a handler 
counts the `SIGPIPE` signals), QEMU with HVF, with `CONFIG_NET`, 
`CONFIG_NET_TCP`, `CONFIG_NET_IPv4`, `CONFIG_NET_LOOPBACK`, 
`CONFIG_NETDEV_LATEINIT`, `CONFIG_NET_SOCKOPTS`, `CONFIG_SIG_DEFAULT`, 
`CONFIG_SIG_SIGPIPE_ACTION`, `CONFIG_SCHED_WAITPID`, 
`CONFIG_SCHED_CHILD_STATUS`, `CONFIG_FS_TMPFS`, `CONFIG_IOB_NBUFFERS=96`, 
`CONFIG_NET_RECV_BUFSIZE=4096`, in five configurations:
   
   - qemu-armv8a:knsh (kernel build): unbuffered TCP with 
`CONFIG_NET_SENDFILE`; `CONFIG_NET_TCP_WRITE_BUFFERS` with 
`CONFIG_NET_SENDFILE`; `CONFIG_NET_TCP_WRITE_BUFFERS` without 
`CONFIG_NET_SENDFILE` (the `sendfile()` slow path).
   - qemu-armv8a:nsh (flat build, the test as a builtin app): unbuffered TCP 
and `CONFIG_NET_TCP_WRITE_BUFFERS`, both with `CONFIG_NET_SENDFILE`.
   
   The results are the same in all five configurations:
   
   | Case | master | this PR |
   |---|---|---|
   | `send()` on a socket that never connected | `ENOTCONN`, no signal: PASS | 
`ENOTCONN`, no signal: PASS |
   | `send()` after own `shutdown(SHUT_WR)` | `EPIPE`, 1 signal: PASS | 
`EPIPE`, 1 signal: PASS |
   | `send()` after the peer closed | `ENOTCONN`, no signal: FAIL | `EPIPE`, 1 
signal: PASS |
   | one more `send()` | `ENOTCONN`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `send(MSG_NOSIGNAL)` | `ENOTCONN`: FAIL | `EPIPE`, no signal: PASS |
   | `write()` | `ENOTCONN`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `sendmsg()` | `ENOTCONN`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `sendfile()` from a tmpfs file | `ENOTCONN`, no signal: FAIL | `EPIPE`, 1 
signal: PASS |
   | `recv()` after the reset | 0: PASS | 0: PASS |
   | `poll()` after the reset | `POLLHUP` set (revents 0x19): PASS | the same: 
PASS |
   | a thread blocked in a 32 KiB `send()` when the peer closes | `ENOTCONN`, 
no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | a child without a handler (default action) sends after the peer closed | 
exits with status 0: FAIL | ends (wait status 0x100): PASS |
   
   ostest exits with status 0 in all five configurations and on qemu-armv8a:nsh 
with the same options, on master and with this PR.
   
   The NFS commit is checked by reading the code only: no configuration here 
has an NFS server.
   
   Build with `EXTRAFLAGS="-Wno-cpp -Werror"`, each OK: qemu-armv8a:netnsh 
(flat, arm64, with `CONFIG_NFS`, `CONFIG_NET_SENDFILE`, 
`CONFIG_SIG_SIGPIPE_ACTION`); rv-virt:netnsh64 (flat, rv64, `CONFIG_NFS`, 
`CONFIG_NET_SENDFILE`, `CONFIG_NET_TCP_WRITE_BUFFERS`); rv-virt:knetnsh64 
(kernel, rv64, `CONFIG_NFS`, `CONFIG_NET_SENDFILE`); rv-virt:pnsh64 (protected, 
rv64) with the network options and `CONFIG_NET_SENDFILE`; zcu111:netnsh 
(arm64); spresense:rndis (arm, `CONFIG_NFS`); sama5d2-xult:netnsh (arm, 
`CONFIG_NET_SENDFILE`; the link is OK, only the final `mkimage` step is missing 
on the build host). qemu-armv8a:nsh with the network options, 
`CONFIG_NET_SENDFILE` and `CONFIG_DISABLE_ALL_SIGNALS`: the changed files 
compile without warnings; the link fails because `init_main` and `romfs_img` 
are not built in this configuration, not in the changed code.
   
   `tools/checkpatch.sh -c -u -m -g` passes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to