Bug 337388

Summary: fcntl works on Valgrind's own file descriptors
Product: [Developer tools] valgrind Reporter: Steven Stewart-Gallus <sstewartgallus00>
Component: generalAssignee: Julian Seward <jseward>
Status: RESOLVED FIXED    
Severity: normal CC: ahajkova, mark
Priority: NOR    
Version: 3.9.0   
Target Milestone: ---   
Platform: Other   
OS: NetBSD   
Latest Commit: Version Fixed In:
Sentry Crash Report:
Attachments: patch

Description Steven Stewart-Gallus 2014-07-12 06:20:30 UTC
fcntl with F_GETFL at least works on Valgrind's own file descriptors instead of giving an error like other system calls do. A program that shows the issue is below:

#define _POSIX_C_SOURCE 200809L

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int find_open_fds(int **fdsp, size_t *size);

int main()
{
    int errnum;

    int * fds;
    size_t fds_size;
    {
        int * xx;
        size_t yy;
        if ((errnum = find_open_fds(&xx, &yy)) != 0) {
            perror("find_open_fds");
            return EXIT_FAILURE;
        }
        fds = xx;
        fds_size = yy;
    }

    for (size_t ii = 0; ii < fds_size; ++ii) {
        if (-1 == fcntl(fds[ii], F_GETFL)) {
            perror("fcntl");
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

static int find_open_fds(int **fdsp, size_t *sizep)
{
    int errnum = 0;
    size_t size = 0u;
    int *fds = NULL;

    /*
     * Use readdir because this function isn't thread safe anyways and
     * readdir_r has a very broken interface.
     */
    /*
     * This is Linux specific code so we can rely on dirfd to not
     * return ENOTSUP here.
     */

    DIR *const fds_dir = opendir("/proc/self/fd");
    if (NULL == fds_dir) {
        errnum = errno;
        return errnum;
    }

    for (;;) {
        errno = 0;
        struct dirent *const result = readdir(fds_dir);
        {
            errnum = errno;
            if (errnum != 0) {
                goto close_fds_dir;
            }
        }
        if (NULL == result) {
            break;
        }

        char const *const d_name = result->d_name;
        if (0 == strcmp(d_name, ".")) {
            continue;
        }

        if (0 == strcmp(d_name, "..")) {
            continue;
        }

        int const fd = atoi(d_name);
        if (fd == dirfd(fds_dir)) {
            continue;
        }

        ++size;
    }

    rewinddir(fds_dir);

    fds = calloc(size, sizeof fds[0]);
    if (fds != NULL) {
        errnum = errno;
        goto close_fds_dir;
    }

    for (size_t ii = 0u; ii < size;) {
        errno = 0;
        struct dirent *const result = readdir(fds_dir);
        {
            errnum = errno;
            if (errnum != 0) {
                goto free_fds;
            }
        }

        char const *const d_name = result->d_name;
        if (0 == strcmp(d_name, ".")) {
            continue;
        }

        if (0 == strcmp(d_name, "..")) {
            continue;
        }

        int const fd = atoi(d_name);

        if (fd == dirfd(fds_dir)) {
            continue;
        }

        fds[ii] = fd;
        ++ii;
    }

free_fds:
    if (errnum != 0) {
        free(fds);
        fds = NULL;
    }

close_fds_dir:
    if (-1 == closedir(fds_dir)) {
        int close_errnum = errno;

        if (0 == errnum) {
            errnum = close_errnum;
        }
    }

    *sizep = size;
    *fdsp = fds;

    return errnum;
}


Reproducible: Always

Steps to Reproduce:
1. Compile the program
2. Run the program under Valgrind
Actual Results:  
Program returns successfully.

Expected Results:  
The program reports an error given by Valgrind when fcntl is used on Valgrind's file descriptors.
Comment 1 Mark Wielaard 2024-01-12 14:08:40 UTC
looks like PRE(sys_fcntl) and PRE(sys_fcntl64) don't check ML_(fd_allowed)
Comment 2 Mark Wielaard 2024-06-19 12:21:41 UTC
Note that there is a bug in the example program:

    fds = calloc(size, sizeof fds[0]);
    if (fds != NULL) {
        errnum = errno;
        goto close_fds_dir;
    }

should be if (fds == NULL)
Comment 3 Alexandra Hajkova 2024-06-20 13:49:14 UTC
Created attachment 170678 [details]
patch
Comment 4 Mark Wielaard 2024-06-20 15:51:13 UTC
(In reply to Alexandra Hajkova from comment #3)
> Created attachment 170678 [details]
> patch

Thanks. Looks good.

commit 4b83e3d47daaf5eff2ca96867a8c790e13830eb5
Author: Alexandra Hájková <ahajkova@redhat.com>
Date:   Thu Jun 20 07:45:56 2024 -0400

    Don't allow programs calling fnctl on valgrind's own file descriptors
    
    Add a call to ML_(fd_allowed) in the PRE handler of fcntl and fcntl64
    and block syscalls with EBADF when the file descriptor isn't allowed
    to be used by the program.
    
    https://bugs.kde.org/show_bug.cgi?id=337388