NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/54435: reading TCP urgent data with MSG_OOB doesn't clear poll(2) status



The following reply was made to PR kern/54435; it has been noted by GNATS.

From: Christos Zoulas <christos%zoulas.com@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: kern-bug-people%netbsd.org@localhost,
 gnats-admin%netbsd.org@localhost,
 netbsd-bugs%netbsd.org@localhost
Subject: Re: kern/54435: reading TCP urgent data with MSG_OOB doesn't clear
 poll(2) status
Date: Thu, 8 Aug 2019 19:07:54 +0300

 Darwin vpn1-1.astron.com 18.7.0 Darwin Kernel Version 18.7.0: Thu Jun 20 =
 18:42:21 PDT 2019; root:xnu-4903.270.47~4/RELEASE_X86_64 x86_64
 reading urgent data with MSG_OOB
 poll: revents =3D 0x93: HUP IN PRI RDBAND
 recv(MSG_OOB) =3D 1
 b
 poll: revents =3D 0x93: HUP IN PRI RDBAND
 recv(MSG_OOB) failed, retrying
 recv() =3D 1
 a
 poll: revents =3D 0x93: HUP IN PRI RDBAND
 recv(MSG_OOB) failed, retrying
 recv() =3D 1
 c
 poll: revents =3D 0x93: HUP IN PRI RDBAND
 recv(MSG_OOB) failed, retrying
 recv() =3D 0
 
 FreeBSD mb1.astron.com 12.0-CURRENT FreeBSD 12.0-CURRENT #0 r318137: Wed =
 May 10 15:09:31 UTC 2017     =
 root%releng3.nyi.freebsd.org@localhost:/usr/obj/usr/src/sys/GENERIC  amd64
 reading urgent data with MSG_OOB
 poll: revents =3D 0x1: IN
 recv() =3D 1
 a
 poll: revents =3D 0x83: IN PRI RDBAND
 recv(MSG_OOB) =3D 1
 b
 poll: revents =3D 0x83: IN PRI RDBAND
 recv(MSG_OOB) failed, retrying
 recv() =3D 1
 c
 poll: revents =3D 0x1: IN
 recv() =3D 0
 
 Linux mb1 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64 =
 GNU/Linux
 reading urgent data with MSG_OOB
 poll: revents =3D 0x3: IN PRI
 recv(MSG_OOB) =3D 1
 b
 poll: revents =3D 0x1: IN
 recv() =3D 1
 a
 poll: revents =3D 0x1: IN
 recv() =3D 1
 c
 poll: revents =3D 0x1: IN
 recv() =3D 0
 
 SunOS openindiana 5.11 illumos-6ccda740e0 i86pc i386 i86pc
 christos@openindiana:~/oob$ ./oobrecv=20
 reading urgent data with MSG_OOB
 poll: revents =3D 0x83: IN PRI RDBAND
 recv(MSG_OOB) =3D 1
 b
 poll: revents =3D 0x83: IN PRI RDBAND
 recv(MSG_OOB) failed, retrying
 recv() =3D 1
 a
 poll: revents =3D 0x83: IN PRI RDBAND
 recv(MSG_OOB) failed, retrying
 recv() =3D 1
 c
 poll: revents =3D 0x1: IN
 recv() =3D 0
 
 ------------------------------------------
 for portability on Solaris:
 #include <sys/param.h>
 #ifdef BSD4_4
 for socklen
 #endif
 
 #include <sys/sockio.h> for SIOCATMARK.
 
 I support the change to make us behave like Linux.
 
 christos
 
 > On Aug 3, 2019, at 5:50 PM, uwe%stderr.spb.ru@localhost wrote:
 >=20
 >> Number:         54435
 >> Category:       kern
 >> Synopsis:       reading TCP urgent data with MSG_OOB doesn't clear =
 poll(2) status
 >> Confidential:   no
 >> Severity:       non-critical
 >> Priority:       low
 >> Responsible:    kern-bug-people
 >> State:          open
 >> Class:          sw-bug
 >> Submitter-Id:   net
 >> Arrival-Date:   Sat Aug 03 14:50:00 +0000 2019
 >> Originator:     Valery Ushakov
 >> Release:        NetBSD-8
 >> Organization:
 >> Environment:
 > NetBSD pony 8.1_STABLE NetBSD 8.1_STABLE (GENERIC) #0: Sat Jun 15 =
 07:30:17 MSK 2019  =
 uwe@sampo:/home/uwe/work/netbsd/build8/obj/macppc/sys/arch/macppc/compile/=
 GENERIC macppc
 >=20
 >> Description:
 > It looks like POLLPRI | POLLRDBAND status in not cleared from the
 > socket when TCP urgent data is read with MSG_OOB
 >=20
 >> How-To-Repeat:
 > cat >> oobserv.c <<'__EOF__'
 > #include <sys/types.h>
 > #include <sys/socket.h>
 >=20
 > #include <netinet/in.h>
 > #include <arpa/inet.h>
 >=20
 > #include <err.h>
 > #include <errno.h>
 > #include <stdio.h>
 > #include <stdlib.h>
 > #include <string.h>
 > #include <unistd.h>
 >=20
 >=20
 > int
 > main(void)
 > {
 >    int status;
 >=20
 >    int server =3D socket(PF_INET, SOCK_STREAM, 0);
 >    if (server < 0)
 > 	err(EXIT_FAILURE, "socket");
 >=20
 >    struct sockaddr_in sin;
 >    memset(&sin, 0, sizeof(sin));
 > #if !defined(__linux__) && !defined(__sun__)
 >    sin.sin_len =3D sizeof(sin);
 > #endif
 >    sin.sin_family =3D AF_INET;
 >    sin.sin_addr.s_addr =3D htonl(INADDR_LOOPBACK);
 >    sin.sin_port =3D htons(12345);
 >=20
 >    status =3D bind(server, (struct sockaddr *)&sin, sizeof(sin));
 >    if (status < 0)
 > 	err(EXIT_FAILURE, "bind");
 >=20
 >    status =3D listen(server, 1);
 >    if (status < 0)
 > 	err(EXIT_FAILURE, "listen");
 >=20
 >    for (;;) {
 > 	int client =3D accept(server, NULL, 0);
 > 	if (client < 0)
 > 	    err(EXIT_FAILURE, "accept");
 >=20
 > 	send(client, "a", 1, 0);
 > 	send(client, "b", 1, MSG_OOB);
 > 	send(client, "c", 1, 0);
 >=20
 > 	close(client);
 >    }
 >=20
 >    return 0;
 > }
 > __EOF__
 >=20
 > cat >> oobrecv.c <<'__EOF__'
 > #include <sys/types.h>
 > #include <sys/socket.h>
 >=20
 > #include <err.h>
 > #include <errno.h>
 > #include <fcntl.h>
 > #include <poll.h>
 > #include <stdbool.h>
 > #include <stdio.h>
 > #include <stdlib.h>
 > #include <string.h>
 > #include <unistd.h>
 >=20
 > #include <netinet/in.h>
 > #include <arpa/inet.h>
 >=20
 > int usage(void);
 > int getsocket(void);
 > void oobtest(int);
 > int pollsock(int);
 >=20
 > bool oobinline =3D false;
 >=20
 > int
 > usage(void)
 > {
 >    fprintf(stderr, "usage: oobrecv [oob | inline]\n");
 >    fprintf(stderr, "default is \"%s\"\n",
 > 	    oobinline ? "inline" : "oob");
 >=20
 >    return EXIT_FAILURE;
 > }
 >=20
 >=20
 > int
 > main(int argc, char **argv)
 > {
 >    if (argc > 1) {
 > 	if (strcasecmp(argv[1], "oob") =3D=3D 0)
 > 	    oobinline =3D false;
 > 	else if (strcasecmp(argv[1], "inline") =3D=3D 0)
 > 	    oobinline =3D true;
 > 	else
 > 	    return usage();
 >    }
 >=20
 >    oobtest(getsocket());
 >    return 0;
 > }
 >=20
 >=20
 > int
 > getsocket(void)
 > {
 >    int s;
 >    int status;
 >=20
 >    s =3D socket(PF_INET, SOCK_STREAM, 0);
 >    if (s < 0)
 > 	err(EXIT_FAILURE, "socket");
 >=20
 >    struct sockaddr_in sin;
 >    memset(&sin, 0, sizeof(sin));
 > #if !defined(__linux__) && !defined(__sun__)
 >    sin.sin_len =3D sizeof(sin);
 > #endif
 >    sin.sin_family =3D AF_INET;
 >    sin.sin_addr.s_addr =3D htonl(INADDR_LOOPBACK);
 >    sin.sin_port =3D htons(12345);
 >=20
 >    if (oobinline) {
 > 	int one =3D 1;
 > 	status =3D setsockopt(s, SOL_SOCKET, SO_OOBINLINE,
 > 			    (char *)&one, sizeof(one));
 > 	if (status < 0)
 > 	    err(EXIT_FAILURE, "SO_OOBINLINE");
 >    }
 >=20
 >    status =3D connect(s, (struct sockaddr *)&sin, sizeof(sin));
 >    if (status < 0)
 > 	err(EXIT_FAILURE, "connect");
 >=20
 >    return s;
 > }
 >=20
 >=20
 > void
 > oobtest(int s)
 > {
 >    char buf[128];
 >    int status;
 >=20
 >    printf("reading urgent data with %s\n",
 > 	   oobinline ? "SO_OOBINLINE" : "MSG_OOB");
 >=20
 >    for (;;) {
 > 	int revents =3D pollsock(s);
 > 	if (revents =3D=3D 0)
 > 	    continue;
 >=20
 > 	if (revents & POLLNVAL) {
 > 	    errno =3D EBADF;
 > 	    err(EXIT_FAILURE, "poll");
 > 	}
 >=20
 > 	if (revents & POLLERR) {
 > 	    int sockerr;
 > 	    socklen_t optlen =3D (socklen_t)sizeof(sockerr);
 >=20
 >            status =3D getsockopt(s, SOL_SOCKET, SO_ERROR,
 > 				(char *)&sockerr, &optlen);
 > 	    if (status < 0)
 > 		err(EXIT_FAILURE, "SO_ERROR");
 >=20
 > 	    errno =3D sockerr;
 > 	    err(EXIT_FAILURE, NULL);
 > 	}
 >=20
 > 	int flags =3D 0;
 > 	if (!oobinline && (revents & (POLLPRI | POLLRDBAND)))
 > 	    flags =3D MSG_OOB;
 >=20
 > 	ssize_t nread =3D recv(s, buf, sizeof(buf), flags);
 > 	if (nread < 0)
 > 	    err(EXIT_FAILURE, "recv%s",
 > 		flags & MSG_OOB ? "(MSG_OOB)" : "");
 >=20
 > 	printf("recv(%s) =3D %zd\n",
 > 	       flags & MSG_OOB ? "MSG_OOB" : "",
 > 	       nread);
 >=20
 > 	if (nread =3D=3D 0)
 > 	    return;
 >=20
 > 	fwrite(buf, 1, nread, stdout);
 > 	printf("\n");
 >    }
 > }
 >=20
 >=20
 > int
 > pollsock(int s)
 > {
 >    struct pollfd fds[1];
 >=20
 >    fds[0].fd =3D s;
 >    fds[0].events =3D POLLIN | POLLPRI | POLLRDBAND;
 >    fds[0].revents =3D 0;
 >=20
 >    int nready =3D poll(fds, 1, -1);
 >    if (nready < 0)
 > 	err(EXIT_FAILURE, "poll");
 >=20
 >    if (nready =3D=3D 0)
 > 	return 0;
 >=20
 >    int revents =3D fds[0].revents;
 >=20
 >    printf("poll: revents =3D 0x%x", revents);
 >    if (fds[0].revents !=3D 0) {
 > 	printf(":");
 > 	if (revents & POLLNVAL)   printf(" NVAL");
 > 	if (revents & POLLERR) 	  printf(" ERR");
 > 	if (revents & POLLHUP) 	  printf(" HUP");
 > 	if (revents & POLLIN)  	  printf(" IN");
 > 	if (revents & POLLPRI) 	  printf(" PRI");
 > 	if (revents & POLLRDNORM) printf(" RDNORM");
 > 	if (revents & POLLRDBAND) printf(" RDBAND");
 >    }
 >    printf("\n");
 >=20
 >    return revents;
 > }
 > __EOF__
 >=20
 > cc -o oobserv oobserv.c
 > cc -o oobrecv oobrecv.c
 >=20
 > ./oobserv &
 > ./oobrecv oob
 >=20
 >=20
 > The server sends "a", then send "b" as urgent/MSG_OOB, then "c".
 >=20
 > The test invocation fails with:
 >=20
 > reading urgent data with MSG_OOB
 > poll: revents =3D 0x1: IN
 > recv() =3D 1
 > a
 > poll: revents =3D 0x82: PRI RDBAND
 > recv(MSG_OOB) =3D 1
 > b
 > poll: revents =3D 0x82: PRI RDBAND
 > oobrecv: recv(MSG_OOB): Invalid argument
 >=20
 > So after "b" was read (with MSG_OOB), the poll still reports POLLPRI.
 >=20
 > This doesn't happen with SO_OOBINLINE:
 >=20
 > $ ./oobrecv inline
 > reading urgent data with SO_OOBINLINE
 > poll: revents =3D 0x1: IN
 > recv() =3D 1
 > a
 > poll: revents =3D 0x83: IN PRI RDBAND
 > recv() =3D 1
 > b
 > poll: revents =3D 0x1: IN
 > recv() =3D 1
 > c
 > poll: revents =3D 0x1: IN
 > recv() =3D 0
 >=20
 > here POLLPRI | POLLRDBAND are cleared after "b" was read.
 >=20
 >> Fix:
 


Home | Main Index | Thread Index | Old Index