On Wed, 3 Apr 2024 17:28:52 +0200
Michael Tuexen <[email protected]> wrote:
> > On 3. Apr 2024, at 15:44, Sad Clouds <[email protected]> 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);
}