Date: Wed, 27 Feb 2002 19:39:38 -0800 (PST) From: rfg@monkeys.com To: FreeBSD-gnats-submit@freebsd.org Subject: kern/35396: poll(2) doesn't set POLLERR for failed connect(2) attempts Message-ID: <20020228033938.A169F660B@segfault.monkeys.com>
next in thread | raw e-mail | index | archive | help
>Number: 35396 >Category: kern >Synopsis: poll(2) doesn't set POLLERR for failed connect(2) attempts >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed Feb 27 19:40:01 PST 2002 >Closed-Date: >Last-Modified: >Originator: Ronald F. Guilmette >Release: FreeBSD 4.3-RELEASE i386 >Organization: Infinite Monkeys & Co. >Environment: FreeBSD 4.3-RELEASE #0 >Description: If you create a socket and then use fcntl(2) to set O_NONBLOCK for it (thus making it non-blocking) and then you start up an asynchronous connect(2) on the socket to some dead host/port, and then use poll(2) to try to check status on the state of the socket, eventually, the connect attempt will actually fail (after a suitable number of failed retries) and the call to poll(2) will return, but in the specific `struct pollfd' structure corresponding to the socket's fd, the POLLERR bit will _not_ be set in the `revents' field. That (POLLERR) bit _should_ be set when such errors occur however. The behavion of poll(2) under FreeBSD 4.3 is clearly not correct behavior. The connect attempt _has_ failed with an error. But poll(2) is failing to properly set the corresponding POLLERR bit to note that fact. (See my posting today to freebsd-questions for a bit more background on this problem.) >How-To-Repeat: The short example program below illustrates the problem. It attempts to connect to a dead/filtered port on one of my servers. This program should (after a short delay) print "poll(2) indicates connect error", but instead it prints "getsockopt(2) indicates connect error", thus indicating that the connection error has _not_ been flaged via a POLLERR bit being set. cut here ======================================================================= /* poll(2) error test #1 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <poll.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> static struct protoent *tcp_proto; static void fatal (register char const *const fmt, register char const *const arg) { fprintf (stderr, fmt, arg); putc ('\n', stderr); exit (1); } static void poll_for_completion (register int const fd) { auto struct pollfd pfd; auto int err; auto socklen_t err_size; pfd.fd = fd; pfd.events = POLLOUT; pfd.revents = 0; if (poll (&pfd, 1, -1) == -1) fatal ("Error in poll: %s", strerror (errno)); if (pfd.revents & POLLERR) fatal ("poll(2) indicates connect error", NULL); if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &err, &err_size) == -1) fatal ("Error in getsockopt: %s", strerror (errno)); if (err != 0) fatal ("getsockopt(2) indicates connect error: %s", strerror (err)); fatal ("Connect successful", NULL); } static void start_connecting (struct in_addr addr, unsigned short port) { auto struct sockaddr_in sin; register int fd; if ((fd = socket (PF_INET, SOCK_STREAM, tcp_proto->p_proto)) == -1) fatal ("Error creating socket: %s", strerror (errno)); if (fcntl (fd, F_SETFL, O_NONBLOCK) == -1) fatal ("Error setting O_NONBLOCK for socket: %s", strerror (errno)); memset (&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr = addr; sin.sin_port = htons (port); if (connect (fd, (struct sockaddr *) &sin, sizeof sin) == -1) { if (errno != EINPROGRESS) { printf ("Connection failed immediately\n"); close (fd); } else poll_for_completion (fd); } else { printf ("Connection completed immediately\n"); close (fd); } } int main (void) { static char const protocol_name[] = "tcp"; auto struct in_addr addr; if ((tcp_proto = getprotobyname (protocol_name)) == NULL) fatal ("Cannot find number for protocol: %s", protocol_name); inet_aton ("66.60.157.246", &addr); start_connecting (addr, 32767); return 0; } ======================================================================= >Fix: Hack the kernel and make poll(2) properly check for connect(2) errors. If it finds any, have it set the POLLERR bit in the relevant `struct pollfd' structure. >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020228033938.A169F660B>