Search code examples
cmacos-sierra

poll() returns EINVAL for more than 256 descriptors on macOS


Here is the example code that crashes:

#include <stdio.h>
#include <poll.h>
#include <stdlib.h>
#include <limits.h>

#define POLL_SIZE 1024

int main(int argc, const char * argv[]) {
    printf("%d\n", OPEN_MAX);
    struct pollfd *poll_ = calloc(POLL_SIZE, sizeof(struct pollfd));
    if (poll(poll_, POLL_SIZE, -1) < 0)
        if (errno == EINVAL)
            perror("poll error");
    return 0;
}

If you set POLL_SIZE to 256 or less, the code works just fine. What's interesting is that if you run this code in Xcode, it executes normally, but if you run the binary yourself you get a crash.

The output is like this:

10240
poll error: Invalid argument

According to poll(2):

[EINVAL] The nfds argument is greater than OPEN_MAX or the
         timeout argument is less than -1.

As you can see, POLL_SIZE is a lot smaller than the limit, and the timeout is exactly -1, yet it crashed.

My clang version that I use for manual building:

Configured with: prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.41)
Target: x86_64-apple-darwin16.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Solution

  • On Unix systems, processes have limits on resources. See e.g. getrlimit. You might change them (using setrlimit) and your sysadmin can also change them (e.g. configure these limits at startup or login time). There is a limit RLIMIT_NOFILE related to file descriptors. Read also about the ulimit bash builtin. See also sysconf with _SC_OPEN_MAX.

    The poll system call is given a not-too-big array, and it is bad taste (possible, but inefficient) to repeat some file descriptor in it. So in practice you'll often use it with a quite small array mentioning different (but valid) file descriptors. The second argument to poll is the number of useful entries (practically, all different), not the allocated size of the array.

    You may deal with many file descriptors. Read about the C10K problem.

    BTW, your code is not crashing. poll is failing as documented (but not crashing).

    You should read some POSIX programming book. Advanced Linux Programming is freely available, and most of it is on POSIX (not Linux specific) things.