Bug 507869

Summary: Various at syscalls don't check dirfd argument
Product: [Developer tools] valgrind Reporter: Mark Wielaard <mark>
Component: generalAssignee: Alexandra Hajkova <ahajkova>
Status: RESOLVED FIXED    
Severity: normal CC: ahajkova, mcermak, pjfloyd
Priority: NOR    
Version First Reported In: 3.25 GIT   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:

Description Mark Wielaard 2025-08-04 22:41:22 UTC
int utimensat(int dirfd, const char *pathname,
                     const struct timespec times[_Nullable 2], int flags);

The PRE handler in for sys_utimensat and sys_utimensat_time64 don't check the dirfd (ARG1) argument with fd_allowed.

Note that like other at handlers it has to accept VKI_AT_FDCWD and ignore dirfd if the pathname is absolute.

There is a test inside memcheck/tests/linux/syscalls-2007.c
There is also an LTP testcase utimensat01 but that needs privileges to run.
Comment 1 Mark Wielaard 2025-08-04 23:14:04 UTC
It isn't just utimensat, the following at syscalls also don't check their dirfd argument:

int mkdirat(int dirfd, const char *pathname, mode_t mode);
int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
int fstatat(int dirfd, const char *restrict pathname, struct stat *restrict statbuf, int flags);
(there is also a newfstatat syscall variant)
int unlinkat(int dirfd, const char *pathname, int flags);
int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
int renameat2(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags);
int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags);
int symlinkat(const char *target, int newdirfd, const char *linkpath);
ssize_t readlinkat(int dirfd, const char *restrict pathname, char *restrict buf, size_t bufsiz);
Comment 2 Mark Wielaard 2025-08-28 10:53:24 UTC
commit 456b68ece180a49595a238a4f1105105cd7025ca
Author: Paul Floyd <pjfloyd@wanadoo.fr>
Date:   Tue Aug 26 21:56:55 2025 +0200

    Linux syscalls: add checks for remaining *at() directory fd args

Handles mkdirat, mknodat, utimensat, newfstatat, unlinkat, renameat, linkat, symlinkat, readlinkat

I believe this just leaves fstatat64 (in some 32bit linux syswraps) and renameat2
Comment 3 Paul Floyd 2025-08-28 12:51:54 UTC
I was searching for "at(". I'll look at the others.
Comment 4 Paul Floyd 2025-08-29 05:44:40 UTC
Should all be done, but I'd like to refactor the code before closing this.
Comment 5 Paul Floyd 2025-08-29 06:49:52 UTC
commit 9f5d3d32b5bd5bc546eea74a8f75e53d4a228519
Author: Paul Floyd <pjfloyd@wanadoo.fr>
Date:   Fri Aug 29 07:59:28 2025 +0200

    Linux FreeBSD and Darwin: refactor *at syscall dirfd checks
    
    I haven't done Solaris. The code there is less messy because
    Solaris doesn't use a negative value for AT_FDCWD, meaning
    no explicit or implicit cast from unsigned word to signed
    int is needed before comparing to the int dirfd parameter.