NetBSD-Bugs archive

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

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



>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