NetBSD Problem Report #54650
From www@netbsd.org Sat Oct 26 21:49:11 2019
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id B5B677A158
for <gnats-bugs@gnats.NetBSD.org>; Sat, 26 Oct 2019 21:49:11 +0000 (UTC)
Message-Id: <20191026214911.06ED67A253@mollari.NetBSD.org>
Date: Sat, 26 Oct 2019 21:49:11 +0000 (UTC)
From: cryintothebluesky@gmail.com
Reply-To: cryintothebluesky@gmail.com
To: gnats-bugs@NetBSD.org
Subject: Calling accept() with a non-blocking listening socket, returns a non-blocking connected socket, which is incorrect behaviour
X-Send-Pr-Version: www-1.0
>Number: 54650
>Category: kern
>Synopsis: Calling accept() with a non-blocking listening socket, returns a non-blocking connected socket, which is incorrect behaviour
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Oct 26 21:50:00 +0000 2019
>Last-Modified: Sun Oct 27 13:25:01 +0000 2019
>Originator: Sad Clouds
>Release: 8.1
>Organization:
>Environment:
NetBSD atom510 8.1 NetBSD 8.1 (GENERIC) #0: Fri May 31 14:17:14 BST 2019 roman@z600:/netbsd_build/obj.amd64/sys/arch/amd64/compile/GENERIC amd64
>Description:
Calling accept() with a non-blocking listening socket, returns a non-blocking connected socket, which is incorrect behaviour. The new returned connected socket should be blocking.
>How-To-Repeat:
>Fix:
>Audit-Trail:
From: Sad Clouds <cryintothebluesky@gmail.com>
To: gnats-bugs@netbsd.org
Cc: gnats-admin@netbsd.org
Subject: Re: kern/54650: Calling accept() with a non-blocking listening
socket, returns a non-blocking connected socket, which is incorrect
behaviour
Date: Sat, 26 Oct 2019 22:51:45 +0100
This is a multi-part message in MIME format.
--Multipart=_Sat__26_Oct_2019_22_51_45_+0100_yuQIk.oe0XeU2Ov6
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Attached is a test program, this is the output I get on Linux and NetBSD
On Linux:
$ ./a.out
Create server thread
Create client thread
Accept connection, fd=5, addr=127.0.0.1, port=47746
Connected socket is blocking
On NetBSD:
$ ./a.out
Create server thread
Create client thread
Accept connection, fd=5, addr=127.0.0.1, port=65534
Connected socket is non-blocking
--Multipart=_Sat__26_Oct_2019_22_51_45_+0100_yuQIk.oe0XeU2Ov6
Content-Type: text/x-csrc;
name="test.c"
Content-Disposition: attachment;
filename="test.c"
Content-Transfer-Encoding: 7bit
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>
#include <poll.h>
#define ADDR "127.0.0.1"
#define PORT 9999
/* Set fd blocking or non-blocking */
void set_fd_nonblock(int fd, bool set_flag)
{
int ret_int, fcntl_flags;
/* Get current flags */
fcntl_flags = fcntl(fd, F_GETFL, 0);
if (fcntl_flags == -1)
{
fprintf(stderr, "Error %s:%d: fcntl() with F_GETFL failed, %s\n",
__FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (set_flag)
{
/* Set non-blocking */
fcntl_flags |= O_NONBLOCK;
}
else
{
/* Set blocking */
fcntl_flags &= ~O_NONBLOCK;
}
ret_int = fcntl(fd, F_SETFL, fcntl_flags);
if (ret_int == -1)
{
fprintf(stderr, "Error %s:%d: fcntl() with F_SETFL failed, %s\n",
__FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
}
/* Check fd is non-blocking */
bool is_fd_nonblock(int fd)
{
int fcntl_flags;
/* Get current flags */
fcntl_flags = fcntl(fd, F_GETFL, 0);
if (fcntl_flags == -1)
{
fprintf(stderr, "Error %s:%d: fcntl() with F_GETFL failed, %s\n",
__FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
return (fcntl_flags & O_NONBLOCK);
}
/* Client thread */
static void *cli(void *arg)
{
struct sockaddr_in addr;
int conn_fd;
memset(&addr, 0, sizeof(addr));
if (inet_pton(AF_INET, ADDR, &(addr.sin_addr)) != 1)
{
fprintf(stderr, "Error %s:%d: inet_pton() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
conn_fd = socket(AF_INET, SOCK_STREAM, 0);
if (conn_fd < 0)
{
fprintf(stderr, "Error %s:%d: socket() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (connect(conn_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
fprintf(stderr, "Error %s:%d: connect() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
return NULL;
}
/* Server thread */
static void *srv(void *arg)
{
struct sockaddr_in addr, cli_addr;
socklen_t cli_addr_len;
char addr_str[INET6_ADDRSTRLEN];
int ret_int, listen_fd, conn_fd;
ssize_t ret_ssize;
struct pollfd pollfd_array[1];
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
/* The usual - socket, bind, listen */
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
{
fprintf(stderr, "Error %s:%d: socket() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
fprintf(stderr, "Error %s:%d: bind() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (listen(listen_fd, 128) < 0)
{
fprintf(stderr, "Error %s:%d: listen() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
/* Set listening socket non-blocking */
set_fd_nonblock(listen_fd, true);
/* Set poll events */
pollfd_array[0].fd = listen_fd;
pollfd_array[0].events = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI);
/* Block in poll until socket is ready for I/O */
ret_int = poll(pollfd_array, 1, -1);
if (ret_int < 0)
{
fprintf(stderr, "Error %s:%d: poll() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
/* Accept new connection */
cli_addr_len = sizeof(cli_addr);
conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
if (conn_fd < 0)
{
fprintf(stderr, "Error %s:%d: accept() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (inet_ntop(AF_INET, &(cli_addr.sin_addr), addr_str, sizeof(addr_str)) == NULL)
{
fprintf(stderr, "Error %s:%d: inet_ntop() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stdout, "Accept connection, fd=%d, addr=%s, port=%hu\n",
conn_fd, addr_str, ntohs(cli_addr.sin_port));
/* Check if connected socket is non-blocking or blocking */
if (is_fd_nonblock(conn_fd))
{
fprintf(stdout, "Connected socket is non-blocking\n");
}
else
{
fprintf(stdout, "Connected socket is blocking\n");
}
return NULL;
}
int main(void)
{
int ret_int;
pthread_t cli_tid, srv_tid;
fprintf(stdout, "Create server thread\n");
ret_int = pthread_create(&srv_tid, NULL, &srv, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_create() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
sleep(1); /* Give server thread time to setup listening socket */
fprintf(stdout, "Create client thread\n");
ret_int = pthread_create(&cli_tid, NULL, &cli, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_create() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
/* Wait for client to exit */
ret_int = pthread_join(cli_tid, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_join() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
/* Wait for server to exit */
ret_int = pthread_join(srv_tid, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_join() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
}
--Multipart=_Sat__26_Oct_2019_22_51_45_+0100_yuQIk.oe0XeU2Ov6--
From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: kern-bug-people@netbsd.org,
gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org,
cryintothebluesky@gmail.com
Subject: Re: kern/54650: Calling accept() with a non-blocking listening
socket, returns a non-blocking connected socket, which is incorrect behaviour
Date: Sat, 26 Oct 2019 18:37:47 -0400
Linux is unique in that behavior. All the BSDs (including MacOS) and =
Solaris return a non-blocking socket.
christos=
From: Martin Husemann <martin@duskware.de>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: kern/54650: Calling accept() with a non-blocking listening
socket, returns a non-blocking connected socket, which is incorrect
behaviour
Date: Sun, 27 Oct 2019 08:27:29 +0100
On Sat, Oct 26, 2019 at 06:37:47PM -0400, Christos Zoulas wrote:
> Linux is unique in that behavior. All the BSDs (including MacOS) and Solaris return a non-blocking socket.
I think it is also documented in accept(4):
... creates a new socket with the same
properties of s and allocates a new file descriptor for the socket.
Where I read "same properties" as including the socket options.
Martin
From: Sad Clouds <cryintothebluesky@gmail.com>
To: gnats-bugs@netbsd.org
Cc: Martin Husemann <martin@duskware.de>, kern-bug-people@netbsd.org,
gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: kern/54650: Calling accept() with a non-blocking listening
socket, returns a non-blocking connected socket, which is incorrect
behaviour
Date: Sun, 27 Oct 2019 07:59:47 +0000
On Sun, 27 Oct 2019 07:30:02 +0000 (UTC)
Martin Husemann <martin@duskware.de> wrote:
> I think it is also documented in accept(4):
>
> ... creates a new socket with the same
> properties of s and allocates a new file descriptor for the
> socket.
> Where I read "same properties" as including the socket options.
>
> Martin
>
OK, if that is the case, then please close this bug. Just had a look at
OpenBSD accept() man page and it is much more clear there:
"The accept() call extracts the first connection request on the queue of
pending connections, creates a new socket with the same non-blocking
I/O mode as s, and allocates a new file descriptor for the socket with
the close-on-exec flag clear."
The way Linux behaves makes more sense to me, just because the
listening socket is non-blocking, does not mean I want all the accepted
sockets to be also non-blocking. But it looks like another portability
issue we have to deal with on different platforms.
From: Jason Thorpe <thorpej@me.com>
To: Sad Clouds <cryintothebluesky@gmail.com>
Cc: gnats-bugs@netbsd.org,
Martin Husemann <martin@duskware.de>,
kern-bug-people@netbsd.org,
gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org
Subject: Re: kern/54650: Calling accept() with a non-blocking listening
socket, returns a non-blocking connected socket, which is incorrect behaviour
Date: Sun, 27 Oct 2019 06:20:26 -0700
> On Oct 27, 2019, at 12:59 AM, Sad Clouds <cryintothebluesky@gmail.com> =
wrote:
>=20
> On Sun, 27 Oct 2019 07:30:02 +0000 (UTC)
> Martin Husemann <martin@duskware.de> wrote:
>=20
>> I think it is also documented in accept(4):
>>=20
>> ... creates a new socket with the same
>> properties of s and allocates a new file descriptor for the
>> socket.=20
>> Where I read "same properties" as including the socket options.
>>=20
>> Martin
>>=20
There is a lot less text in the SUSv4 description of accept(2):
<quote>
The accept() function shall extract the first connection on the queue of =
pending connections, create a new socket with the same socket type =
protocol and address family as the specified socket, and allocate a new =
file descriptor for that socket. The file descriptor shall be allocated =
as described in File Descriptor Allocation.
</quote>
It does not explicitly allow the historic BSD behavior, nor does it =
explicitly forbid it.
>=20
> OK, if that is the case, then please close this bug. Just had a look =
at
> OpenBSD accept() man page and it is much more clear there:
>=20
> "The accept() call extracts the first connection request on the queue =
of
> pending connections, creates a new socket with the same non-blocking
> I/O mode as s, and allocates a new file descriptor for the socket with
> the close-on-exec flag clear."
>=20
> The way Linux behaves makes more sense to me, just because the
> listening socket is non-blocking, does not mean I want all the =
accepted
> sockets to be also non-blocking. But it looks like another portability
> issue we have to deal with on different platforms.=20
-- thorpej
(Contact us)
$NetBSD: query-full-pr,v 1.43 2018/01/16 07:36:43 maya Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2017
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.