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
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 = 0x93: HUP IN PRI RDBAND
recv(MSG_OOB) = 1
b
poll: revents = 0x93: HUP IN PRI RDBAND
recv(MSG_OOB) failed, retrying
recv() = 1
a
poll: revents = 0x93: HUP IN PRI RDBAND
recv(MSG_OOB) failed, retrying
recv() = 1
c
poll: revents = 0x93: HUP IN PRI RDBAND
recv(MSG_OOB) failed, retrying
recv() = 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 = 0x1: IN
recv() = 1
a
poll: revents = 0x83: IN PRI RDBAND
recv(MSG_OOB) = 1
b
poll: revents = 0x83: IN PRI RDBAND
recv(MSG_OOB) failed, retrying
recv() = 1
c
poll: revents = 0x1: IN
recv() = 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 = 0x3: IN PRI
recv(MSG_OOB) = 1
b
poll: revents = 0x1: IN
recv() = 1
a
poll: revents = 0x1: IN
recv() = 1
c
poll: revents = 0x1: IN
recv() = 0
SunOS openindiana 5.11 illumos-6ccda740e0 i86pc i386 i86pc
christos@openindiana:~/oob$ ./oobrecv
reading urgent data with MSG_OOB
poll: revents = 0x83: IN PRI RDBAND
recv(MSG_OOB) = 1
b
poll: revents = 0x83: IN PRI RDBAND
recv(MSG_OOB) failed, retrying
recv() = 1
a
poll: revents = 0x83: IN PRI RDBAND
recv(MSG_OOB) failed, retrying
recv() = 1
c
poll: revents = 0x1: IN
recv() = 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:
>
>> 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
>
>> Description:
> It looks like POLLPRI | POLLRDBAND status in not cleared from the
> socket when TCP urgent data is read with MSG_OOB
>
>> How-To-Repeat:
> cat >> oobserv.c <<'__EOF__'
> #include <sys/types.h>
> #include <sys/socket.h>
>
> #include <netinet/in.h>
> #include <arpa/inet.h>
>
> #include <err.h>
> #include <errno.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
>
>
> int
> main(void)
> {
> int status;
>
> int server = socket(PF_INET, SOCK_STREAM, 0);
> if (server < 0)
> err(EXIT_FAILURE, "socket");
>
> struct sockaddr_in sin;
> memset(&sin, 0, sizeof(sin));
> #if !defined(__linux__) && !defined(__sun__)
> sin.sin_len = sizeof(sin);
> #endif
> sin.sin_family = AF_INET;
> sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> sin.sin_port = htons(12345);
>
> status = bind(server, (struct sockaddr *)&sin, sizeof(sin));
> if (status < 0)
> err(EXIT_FAILURE, "bind");
>
> status = listen(server, 1);
> if (status < 0)
> err(EXIT_FAILURE, "listen");
>
> for (;;) {
> int client = accept(server, NULL, 0);
> if (client < 0)
> err(EXIT_FAILURE, "accept");
>
> send(client, "a", 1, 0);
> send(client, "b", 1, MSG_OOB);
> send(client, "c", 1, 0);
>
> close(client);
> }
>
> return 0;
> }
> __EOF__
>
> cat >> oobrecv.c <<'__EOF__'
> #include <sys/types.h>
> #include <sys/socket.h>
>
> #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>
>
> #include <netinet/in.h>
> #include <arpa/inet.h>
>
> int usage(void);
> int getsocket(void);
> void oobtest(int);
> int pollsock(int);
>
> bool oobinline = false;
>
> int
> usage(void)
> {
> fprintf(stderr, "usage: oobrecv [oob | inline]\n");
> fprintf(stderr, "default is \"%s\"\n",
> oobinline ? "inline" : "oob");
>
> return EXIT_FAILURE;
> }
>
>
> int
> main(int argc, char **argv)
> {
> if (argc > 1) {
> if (strcasecmp(argv[1], "oob") == 0)
> oobinline = false;
> else if (strcasecmp(argv[1], "inline") == 0)
> oobinline = true;
> else
> return usage();
> }
>
> oobtest(getsocket());
> return 0;
> }
>
>
> int
> getsocket(void)
> {
> int s;
> int status;
>
> s = socket(PF_INET, SOCK_STREAM, 0);
> if (s < 0)
> err(EXIT_FAILURE, "socket");
>
> struct sockaddr_in sin;
> memset(&sin, 0, sizeof(sin));
> #if !defined(__linux__) && !defined(__sun__)
> sin.sin_len = sizeof(sin);
> #endif
> sin.sin_family = AF_INET;
> sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> sin.sin_port = htons(12345);
>
> if (oobinline) {
> int one = 1;
> status = setsockopt(s, SOL_SOCKET, SO_OOBINLINE,
> (char *)&one, sizeof(one));
> if (status < 0)
> err(EXIT_FAILURE, "SO_OOBINLINE");
> }
>
> status = connect(s, (struct sockaddr *)&sin, sizeof(sin));
> if (status < 0)
> err(EXIT_FAILURE, "connect");
>
> return s;
> }
>
>
> void
> oobtest(int s)
> {
> char buf[128];
> int status;
>
> printf("reading urgent data with %s\n",
> oobinline ? "SO_OOBINLINE" : "MSG_OOB");
>
> for (;;) {
> int revents = pollsock(s);
> if (revents == 0)
> continue;
>
> if (revents & POLLNVAL) {
> errno = EBADF;
> err(EXIT_FAILURE, "poll");
> }
>
> if (revents & POLLERR) {
> int sockerr;
> socklen_t optlen = (socklen_t)sizeof(sockerr);
>
> status = getsockopt(s, SOL_SOCKET, SO_ERROR,
> (char *)&sockerr, &optlen);
> if (status < 0)
> err(EXIT_FAILURE, "SO_ERROR");
>
> errno = sockerr;
> err(EXIT_FAILURE, NULL);
> }
>
> int flags = 0;
> if (!oobinline && (revents & (POLLPRI | POLLRDBAND)))
> flags = MSG_OOB;
>
> ssize_t nread = recv(s, buf, sizeof(buf), flags);
> if (nread < 0)
> err(EXIT_FAILURE, "recv%s",
> flags & MSG_OOB ? "(MSG_OOB)" : "");
>
> printf("recv(%s) = %zd\n",
> flags & MSG_OOB ? "MSG_OOB" : "",
> nread);
>
> if (nread == 0)
> return;
>
> fwrite(buf, 1, nread, stdout);
> printf("\n");
> }
> }
>
>
> int
> pollsock(int s)
> {
> struct pollfd fds[1];
>
> fds[0].fd = s;
> fds[0].events = POLLIN | POLLPRI | POLLRDBAND;
> fds[0].revents = 0;
>
> int nready = poll(fds, 1, -1);
> if (nready < 0)
> err(EXIT_FAILURE, "poll");
>
> if (nready == 0)
> return 0;
>
> int revents = fds[0].revents;
>
> printf("poll: revents = 0x%x", revents);
> if (fds[0].revents != 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");
>
> return revents;
> }
> __EOF__
>
> cc -o oobserv oobserv.c
> cc -o oobrecv oobrecv.c
>
> ./oobserv &
> ./oobrecv oob
>
>
> The server sends "a", then send "b" as urgent/MSG_OOB, then "c".
>
> The test invocation fails with:
>
> reading urgent data with MSG_OOB
> poll: revents = 0x1: IN
> recv() = 1
> a
> poll: revents = 0x82: PRI RDBAND
> recv(MSG_OOB) = 1
> b
> poll: revents = 0x82: PRI RDBAND
> oobrecv: recv(MSG_OOB): Invalid argument
>
> So after "b" was read (with MSG_OOB), the poll still reports POLLPRI.
>
> This doesn't happen with SO_OOBINLINE:
>
> $ ./oobrecv inline
> reading urgent data with SO_OOBINLINE
> poll: revents = 0x1: IN
> recv() = 1
> a
> poll: revents = 0x83: IN PRI RDBAND
> recv() = 1
> b
> poll: revents = 0x1: IN
> recv() = 1
> c
> poll: revents = 0x1: IN
> recv() = 0
>
> here POLLPRI | POLLRDBAND are cleared after "b" was read.
>
>> Fix:
Home |
Main Index |
Thread Index |
Old Index