Feature #2998
closedaccept4 returns EPERM instead of ENOSYS on some platforms
Description
I'm working on embedded Freescale ARM Linux, kernel 2.6.35, glibc 2.21. On this platform:
1) accept4() system call is not supported,
2) glibc returns EPERM instead of ENOSYS for missing implementations.
For this reason the fdevent_accept_listenfd() wrapper in fdevent.c does not fallback to calling accept() after trying to call accept4(), and just fails.
After I added || errno == EPERM
to the else if
clause near the line 594, everything works fine.
Updated by gstrauss about 5 years ago
linux kernel 2.6.35 was released 1 Aug 2010, about 9 1/2 years ago. ( https://kernelnewbies.org/LinuxVersions )
glibc 2.21 was released 6 Feb 2015 ( https://www.gnu.org/software/libc/ )
Why such a disparity in release dates?
Updated by gstrauss about 5 years ago
Please double-check your versions and the kernel headers against which you cross-compiled glibc and the kernel.
According to the man page for accept4() on a (modern!) Linux system:
The accept4() system call is available starting with Linux 2.6.28; support in glibc is available starting with version 2.10.
The man page also documents:
In addition, Linux accept() may fail if:
EPERM Firewall rules forbid connection.
Your suggested fix is at odds with the documentation in the man page.
Updated by alex-che almost 5 years ago
The docs was the first thing I checked. I completely agree that EPERM is not the expected error for this case. That's why it took me too much time to find the real reason.
I develop an app which needs to run on an old platform, which I cannot upgrade. A lot of modern libraries won't compile with the original ARM GCC toolchain used for that platform, that's why I use newer toolchain and copy the whole bunch of needed libs, including libc.so, to the platform, so that they are used instead of the original libs of the platform. Hence the version disparity.
As to accept4() syscall availability. According to some sources accept4() was not added to all platforms on 2.6.28. E.g., it was added to ARM only on 2.6.36. Please, see https://lwn.net/Articles/789961/ or just google 'ARM accept4'. Given that my ARM kernel is 2.6.35, it looks likely.
I also found several mentions that on some systems EPERM is incorrectly returned in case of missing syscall table implementation. E.g., see https://github.com/borgbackup/borg/issues/4710 or https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BydOUT4zjxb6QmJWy8U9WbC-q%2BJWV7wLsEY9Df%3Dmw0Mw%40mail.gmail.com#ac8f14897647dc7eae3c7e7cbed36d93 or http://lists.busybox.net/pipermail/buildroot/2011-April/042860.html
Actually, I understand that this case is pretty exotic. And I can understand if you're not willing to change lighttpd code to support undocumented and highly unprobable cases. On the other hand, adding one more error code to the if clause seems to be a tiny code change, which won't break anything, since if EPERM in accept4() is really caused by the firewall (as per documentation), then the accept() should fail with the same error code as well.
Updated by gstrauss almost 5 years ago
- Tracker changed from Bug to Feature
- Status changed from New to Patch Pending
- Target version changed from 1.4.x to 1.4.55
--- a/src/fdevent.c +++ b/src/fdevent.c @@ -629,9 +629,18 @@ int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen fd = -1; } } - } else if (errno == ENOSYS || errno == ENOTSUP) { - fd = accept(listenfd, addr, &len); - sock_cloexec = 0; + } + else { + switch (errno) { + case ENOSYS: + case ENOTSUP: + case EPERM: + fd = accept(listenfd, addr, &len); + sock_cloexec = 0; + break; + default: + break; + } } } else {
Updated by gstrauss almost 5 years ago
- Status changed from Patch Pending to Fixed
- % Done changed from 0 to 100
Applied in changeset fce489b806d82b7c8d59a44e7e394c4aeba8448a.
Also available in: Atom