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>
