diff mbox series

[uclibc-ng-devel] poll: avoid calling select with empty sets which hangs the process

Message ID 20200128142747.2825-1-ysionneau@kalray.eu
State Accepted
Headers show
Series [uclibc-ng-devel] poll: avoid calling select with empty sets which hangs the process | expand

Commit Message

Yann Sionneau Jan. 28, 2020, 2:27 p.m. UTC
From: Yann Sionneau <ysionneau@kalray.eu>

Avoid calling select with empty sets which hangs the process

This makes uClibc-ng act like glibc and musl
Without this fix the test_poll of python3 testsuite hangs forever

Scenario of the issue:
If you call poll with only invalid file descriptors, like in python3
testsuite
(https://github.com/python/cpython/blob/master/Lib/test/test_poll.py#L83)
You will go through uClibc poll emulation code, which is based on
select syscall.

Your first call to select will fail, it will return -1 and errno will be
set to EBADF: https://github.com/wbx-github/uclibc-ng/blob/master/libc/sysdeps/linux/common/poll.c#L120
Then you will go through the for loop which tests individually each file descriptor by calling
select on each one: https://github.com/wbx-github/uclibc-ng/blob/master/libc/sysdeps/linux/common/poll.c#L163
each call will also return -1 with errno being equal to EBADF.
Therefore all pollfd will have the POLLNVAL flag in their respective revents field.
And, the most important, rset/wset/xset will stay empty.

Then the for loop ends, the "continue" makes the while loop run again.
The following select() is run again: https://github.com/wbx-github/uclibc-ng/blob/master/libc/sysdeps/linux/common/poll.c#L120

But this time the sets are empty.
If the poll was called with timeout set to -1, this select will hang forever because there is no timeout
and the sets are empty so no event will ever wake it up.

test program:

int main(void)
{
	struct pollfd pfd;
	int ret;
	int pipe_fds[2];

	pipe(pipe_fds);
	close(pipe_fds[0]);
	close(pipe_fds[1]);

	pfd.fd = pipe_fds[0];
	pfd.events = POLLIN | POLLOUT | POLLPRI;
	pfd.revents = 0;

	ret = poll(&pfd, 1, -1);

	printf("ret: %d\n", ret);
	if (ret < 0)
		printf("error: %s", strerror(errno));
	else {
		puts("revents: ");
		if (pfd.revents & POLLERR)
			printf(" POLLERR");
		if (pfd.revents & POLLHUP)
			printf(" POLLHUP");
		if (pfd.revents & POLLNVAL)
			printf(" POLLNVAL");
		puts("");
	}

	return 0;
}

This hangs on uClibc-ng aarch64 and Kalray's arch (kv3) but does the following on musl and glibc:
"
ret: 1
revents:
 POLLNVAL
"

strace output of this program with uClibc *without* the patch applied:

pselect6(4, [3], [3], [3], NULL, NULL)  = -1 EBADF (Bad file descriptor)
pselect6(4, [3], [3], [3], {tv_sec=0, tv_nsec=0}, NULL) = -1 EBADF (Bad file descriptor)
pselect6(0, 0x7ffffffb80, 0x7ffffffb68, 0x7ffffffb50, NULL, NULL
(never finishes)

strace output of this program with uClibc *with* the patch applied:
pselect6(4, [3], [3], [3], NULL, NULL)  = -1 EBADF (Bad file descriptor)
pselect6(4, [3], [3], [3], {tv_sec=0, tv_nsec=0}, NULL) = -1 EBADF (Bad file descriptor)
write(1, "ret: 1\n", 7ret: 1
)                 = 7
write(1, "revents: \n", 10revents:
)             = 10
write(1, " POLLNVAL\n", 10 POLLNVAL
)             = 10
exit_group(0)                           = ?
+++ exited with 0 +++
---
 libc/sysdeps/linux/common/poll.c | 7 +++++++
 1 file changed, 7 insertions(+)

Comments

Yann Sionneau Jan. 28, 2020, 2:30 p.m. UTC | #1
Hello,

I've sent this patch for review, I didn't run any testsuite yet on it.

But I don't think also that there is much test about poll() in the 
uclibc-ng-testsuite.

I just confirmed it fixes my small reproducer test and the python3 
testsuite.

Any opinion on this fix/issue?

FYI the small test program was not hanging on musl/glibc but was hanging 
on 2 different arch on uClibc-ng (aarch64 and Kalray kv3).

Thank!

Yann

On 1/28/20 3:27 PM, ysionneau@kalray.eu wrote:
> If you call poll with only invalid file descriptors, like in python3
Waldemar Brodkorb Jan. 30, 2020, 8:32 a.m. UTC | #2
Hi,
ysionneau@kalray.eu wrote,

> From: Yann Sionneau <ysionneau@kalray.eu>
> 
> Avoid calling select with empty sets which hangs the process

Thanks, applied and pushed.

best regards
 Waldemar
diff mbox series

Patch

diff --git a/libc/sysdeps/linux/common/poll.c b/libc/sysdeps/linux/common/poll.c
index d1f1f17..3d46d5b 100644
--- a/libc/sysdeps/linux/common/poll.c
+++ b/libc/sysdeps/linux/common/poll.c
@@ -53,6 +53,7 @@  int __NC(poll)(struct pollfd *fds, nfds_t nfds, int timeout)
     fd_set *rset, *wset, *xset;
     struct pollfd *f;
     int ready;
+    int error_num;
     int maxfd = 0;
     int bytes;
 
@@ -142,6 +143,7 @@  int __NC(poll)(struct pollfd *fds, nfds_t nfds, int timeout)
 
 	    /* Reset the return value.  */
 	    ready = 0;
+	    error_num = 0;
 
 	    for (f = fds; f < &fds[nfds]; ++f)
 		if (f->fd != -1 && (f->events & (POLLIN|POLLOUT|POLLPRI))
@@ -178,8 +180,13 @@  int __NC(poll)(struct pollfd *fds, nfds_t nfds, int timeout)
 			    ++ready;
 		    }
 		    else if (errno == EBADF)
+		    {
 			f->revents |= POLLNVAL;
+			error_num++;
+		    }
 		}
+	    if (ready == 0)
+		return error_num;
 	    /* Try again.  */
 	    continue;
 	}