| Summary: | Various at syscalls don't check dirfd argument | ||
|---|---|---|---|
| Product: | [Developer tools] valgrind | Reporter: | Mark Wielaard <mark> |
| Component: | general | Assignee: | 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: | |||
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); 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 I was searching for "at(". I'll look at the others.
Should all be done, but I'd like to refactor the code before closing this. 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. |
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.