On Wed, 3 Apr 2024 17:28:52 +0200 Michael Tuexen <michael.tue...@lurchi.franken.de> wrote:
> > On 3. Apr 2024, at 15:44, Sad Clouds <cryintotheblue...@gmail.com> wrote: > > > > I found a bug that is still open from May 2010 and describes the same > > behaviour that I see with my application: > > > > https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=146845 > > > > If this hasn't been fixed over the last 14 years, then I guess I will > > add some code to simply ignore ECONNRESET on close(2) for FreeBSD and > > MacOS. This seems the be the general advice from other people who hit > > this issue. > I'll bring this up on the bi-weekly FreeBSD transport call. > > Best regards > Michael > > > Hello, I've attached a test program, this easily reproduces the issue on Raspberry Pi 4 within a few seconds of running it. Server output: $ ./econnreset server Server: accept() Server: shutdown() Server: close() .. Server: accept() Server: shutdown() Server: close() close() failed, error=Connection reset by peer Client output (aborts when server exists due to close() failure): $ while true; do ./econnreset client || break; done Client: connect() Client: shutdown() Client: close() .. Client: connect() Assertion failed: (int_val == 0), function client, file econnreset.c, line 156. Abort trap (core dumped)
/* * cc -D_POSIX_C_SOURCE=200809L -D__BSD_VISIBLE -D__XSI_VISIBLE \ * -O2 -std=c11 -Wpedantic -Wall -o econnreset econnreset.c */ #include <arpa/inet.h> #include <assert.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <unistd.h> #define ADDR "127.0.0.1" #define PORT 9999 #define SIZE (10 * 1024U * 1024U) #define CLI_ID "client" #define CLI_ID_LEN 6 #define SRV_ID "server" #define SRV_ID_LEN 6 static void server() { int int_val, sockfd, connfd; struct sockaddr_storage socket_addr = {0}; socklen_t socket_addr_size; uint8_t snd_buf[1024], rcv_buf[1024]; size_t snd_size, rcv_size, io_size; ssize_t ssize_val; /* Socket family */ ((struct sockaddr_in *)&socket_addr)->sin_family = AF_INET; /* Socket address */ int_val = inet_pton(AF_INET, ADDR, &(((struct sockaddr_in *)&socket_addr)->sin_addr)); assert(int_val == 1); /* Socket port */ ((struct sockaddr_in *)&socket_addr)->sin_port = htons(PORT); /* Socket size */ socket_addr_size = sizeof(struct sockaddr_in); sockfd = socket(socket_addr.ss_family, SOCK_STREAM, 0); assert(sockfd >= 0); int_val = bind(sockfd, (const struct sockaddr *)&socket_addr, socket_addr_size); assert(int_val == 0); int_val = listen(sockfd, 128); assert(int_val == 0); while (1) { printf("Server: accept()\n"); connfd = accept(sockfd, NULL, NULL); assert(connfd >= 0); /* Size of data to send/receive */ snd_size = rcv_size = SIZE; /* Data copy loop */ while (snd_size != 0 && rcv_size != 0) { /* Send data */ io_size = (sizeof(snd_buf) <= snd_size) ? sizeof(snd_buf) : snd_size; ssize_val = send(connfd, snd_buf, io_size, MSG_NOSIGNAL); if (ssize_val < 0) { fprintf(stderr, "send() failed, error=%s\n", strerror(errno)); goto cleanup; } snd_size -= (size_t)ssize_val; /* Receive data */ io_size = (sizeof(rcv_buf) <= rcv_size) ? sizeof(rcv_buf) : rcv_size; ssize_val = recv(connfd, rcv_buf, io_size, MSG_NOSIGNAL | MSG_WAITALL); if (ssize_val < 0) { fprintf(stderr, "recv() failed, error=%s\n", strerror(errno)); goto cleanup; } rcv_size -= (size_t)ssize_val; } /* Send server ID string to client */ ssize_val = send(connfd, SRV_ID, SRV_ID_LEN, MSG_NOSIGNAL); if (ssize_val <= 0) { fprintf(stderr, "send() failed, error=%s\n", strerror(errno)); goto cleanup; } /* No more sending, do TCP half close */ printf("Server: shutdown()\n"); int_val = shutdown(connfd, SHUT_WR); if (int_val != 0) { fprintf(stderr, "shutdown() failed, error=%s\n", strerror(errno)); goto cleanup; } /* Receive client ID string and verify it */ ssize_val = recv(connfd, rcv_buf, CLI_ID_LEN, MSG_NOSIGNAL | MSG_WAITALL); if (ssize_val < 0) { fprintf(stderr, "recv() failed, error=%s\n", strerror(errno)); goto cleanup; } assert(ssize_val == CLI_ID_LEN); assert(strncmp((char *)rcv_buf, CLI_ID, CLI_ID_LEN) == 0); cleanup: printf("Server: close()\n"); int_val = close(connfd); if (int_val != 0) { /* We hit https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=146845 */ fprintf(stderr, "close() failed, error=%s\n", strerror(errno)); exit(EXIT_FAILURE); } } } static void client() { int int_val, connfd; struct sockaddr_storage socket_addr = {0}; socklen_t socket_addr_size; uint8_t snd_buf[1024], rcv_buf[1024]; size_t snd_size, rcv_size, io_size; ssize_t ssize_val; /* Socket family */ ((struct sockaddr_in *)&socket_addr)->sin_family = AF_INET; /* Socket address */ int_val = inet_pton(AF_INET, ADDR, &(((struct sockaddr_in *)&socket_addr)->sin_addr)); assert(int_val == 1); /* Socket port */ ((struct sockaddr_in *)&socket_addr)->sin_port = htons(PORT); /* Socket size */ socket_addr_size = sizeof(struct sockaddr_in); connfd = socket(socket_addr.ss_family, SOCK_STREAM, 0); assert(connfd >= 0); printf("Client: connect()\n"); int_val = connect(connfd, (const struct sockaddr *)&socket_addr, socket_addr_size); assert(int_val == 0); /* Size of data to send/receive */ snd_size = rcv_size = SIZE; /* Data copy loop */ while (snd_size != 0 && rcv_size != 0) { /* Send data */ io_size = (sizeof(snd_buf) <= snd_size) ? sizeof(snd_buf) : snd_size; ssize_val = send(connfd, snd_buf, io_size, MSG_NOSIGNAL); assert(ssize_val >= 0); snd_size -= (size_t)ssize_val; /* Receive data */ io_size = (sizeof(rcv_buf) <= rcv_size) ? sizeof(rcv_buf) : rcv_size; ssize_val = recv(connfd, rcv_buf, io_size, MSG_NOSIGNAL | MSG_WAITALL); assert(ssize_val >= 0); rcv_size -= (size_t)ssize_val; } /* Send client ID string to server */ ssize_val = send(connfd, CLI_ID, CLI_ID_LEN, MSG_NOSIGNAL); assert(ssize_val >= 0); /* No more sending, do TCP half close */ printf("Client: shutdown()\n"); int_val = shutdown(connfd, SHUT_WR); assert(int_val == 0); /* Receive server ID string and verify it */ ssize_val = recv(connfd, rcv_buf, SRV_ID_LEN, MSG_NOSIGNAL | MSG_WAITALL); assert(ssize_val == SRV_ID_LEN); assert(strncmp((char *)rcv_buf, SRV_ID, SRV_ID_LEN) == 0); printf("Client: close()\n"); int_val = close(connfd); if (int_val != 0) { /* We hit https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=146845 */ fprintf(stderr, "close() failed, error=%s\n", strerror(errno)); exit(EXIT_FAILURE); } } int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: econnreset client | server\n"); exit(EXIT_FAILURE); } if (strcmp(argv[1], "client") == 0) { client(); } else if (strcmp(argv[1], "server") == 0) { server(); } else { printf("Usage: econnreset client | server\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }