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.
looks like PRE(sys_fcntl) and PRE(sys_fcntl64) don't check ML_(fd_allowed)
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)
Created attachment 170678 [details] patch
(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