fcntl(F_DUPFD) causing apparent file descriptor table corruption

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Al & others

While doing something I shouldn't be doing in my code, I realised that my code 
stopped responding. Turns out that after some F_DUPFD forcing the file 
descriptor preposterously high, the low file descriptors become EBADF. 
Something must be wrong in expand_files, but I don't have the expertise to 
debug it. I'm hoping someone else will.

I've attached a testcase for it. It needs to be run as root. It's not threaded 
and it reproduces the problem 100% of the time on my stable kernel (5.6.13). 
This is not a security issue, since it affects only the calling process 
itself.

On my machine, /proc/sys/fs/nr_open is 1073741816 and I have 32 GB of RAM (if 
the problem is related to memory consumption).

The problem only occurs when growing the table.

strace shows something like:
fcntl(2, F_DUPFD, 1024)                 = 1024
close(1024)                             = 0
fcntl(2, F_DUPFD, 2048)                 = 2048
close(2048)                             = 0
fcntl(2, F_DUPFD, 4096)                 = 4096
close(4096)                             = 0
fcntl(2, F_DUPFD, 8192)                 = 8192
close(8192)                             = 0
fcntl(2, F_DUPFD, 16384)                = 16384
close(16384)                            = 0
fcntl(2, F_DUPFD, 32768)                = 32768
close(32768)                            = 0
fcntl(2, F_DUPFD, 65536)                = 65536
close(65536)                            = 0
fcntl(2, F_DUPFD, 131072)               = 131072
close(131072)                           = 0
fcntl(2, F_DUPFD, 262144)               = 262144
close(262144)                           = 0
fcntl(2, F_DUPFD, 524288)               = 524288
close(524288)                           = 0
fcntl(2, F_DUPFD, 1048576)              = 1048576
close(1048576)                          = 0
fcntl(2, F_DUPFD, 2097152)              = 2097152
close(2097152)                          = 0
fcntl(2, F_DUPFD, 4194304)              = 4194304
close(4194304)                          = 0
fcntl(2, F_DUPFD, 8388608)              = 8388608
close(8388608)                          = 0
fcntl(2, F_DUPFD, 16777216)             = 16777216
close(16777216)                         = 0
fcntl(2, F_DUPFD, 33554432)             = 33554432
close(33554432)                         = 0
fcntl(2, F_DUPFD, 67108864)             = 67108864
close(67108864)                         = 0
fcntl(2, F_DUPFD, 134217728)            = 134217728
close(134217728)                        = 0
fcntl(2, F_DUPFD, 536870912)            = 536870912
close(536870912)                        = 0
write(1, "success\n", 8)                = EBADF
-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel System Software Products
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>

void run_test(int maxfd)
{
    int srcfd = STDERR_FILENO;
    int minfd = 1024;

    /* Linux kernel expands the file descriptor table exponentially, so
     * keep requesting a minimum file descriptor exponentially. */
    for ( ; minfd < maxfd; minfd *= 2) {
        int fd;
        do {
            fd = fcntl(srcfd, F_DUPFD, minfd);
        } while (fd == -1 && errno == EINTR);

        if (fd == -1) {
            if (errno != EMFILE)
                perror("fcntl");
            return;
        }
        close(fd);
    }
}

int main(int argc, char **argv)
{
    struct rlimit lim;
    if (argc > 1) {
        lim.rlim_max = lim.rlim_cur = strtol(argv[1], NULL, 0);
    } else {
        int n;
        FILE *f = fopen("/proc/sys/fs/nr_open","r");
        if (!f) {
            perror("fopen");
            return EXIT_FAILURE;
        }
        if (fscanf(f, "%d", &n) != 1)
            return EXIT_FAILURE;
        fclose(f);
        lim.rlim_max = lim.rlim_cur = n;
    }

    if (setrlimit(RLIMIT_NOFILE, &lim) == -1) {
        perror("setrlimit");
        return EXIT_FAILURE;
    }

    run_test(lim.rlim_cur);

    static const char msg[] = "success\n";
    int ok1 = write(STDOUT_FILENO, msg, strlen(msg)) == strlen(msg);
    return ok1 ? EXIT_SUCCESS : 255;
}

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux