From: Martin Cermak Date: Fri, 1 Aug 2025 13:35:04 +0000 (+0200) Subject: Fix execveat() with AT_FDCWD and relative path, add more checks X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a2c294b3b19ef4af391014e7b3cbc5ec75b2bd68;p=thirdparty%2Fvalgrind.git Fix execveat() with AT_FDCWD and relative path, add more checks This update does address two closely related problems: 1) In case execveat() is called with a special file descriptor value of AT_FDCWD (-100), it should accept this special value, and interpret the provided pathname as relative to the current working directory of the calling process (like execve(2)) instead of failing with EBADF, as it does without this patch. Covered by LTP testcase execveat01. https://bugs.kde.org/show_bug.cgi?id=506806 2) Add checks preventing execveat() of symlinked programs in case AT_SYMLINK_NOFOLLOW was specified. Add checks preventing execveat() from passing in case invalid flag was specified. Covered by LTP testcase execveat02. https://bugs.kde.org/show_bug.cgi?id=506813 --- diff --git a/NEWS b/NEWS index 32bea9353..cf879c8d2 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,8 @@ are not entered into bugzilla tend to get forgotten about or ignored. 506076 unimplemented fcntl command: 1028 (F_CREATED_QUERY) 506499 Unhandled syscall 592 (exterrctl - FreeBSD 506795 Better report which clone flags are problematic +506806 Fix execveat() with AT_FDCWD and relative path +506813 The execveat wrapper needs to do more checking 506910 openat2 with RESOLVE_NO_MAGICLINKS succeeds on /proc/self/exe 506928 Wrap (deprecated) linux specific ustat syscall 506929 Wrap (deprecated) linux sysfs syscall diff --git a/coregrind/m_syswrap/syswrap-linux.c b/coregrind/m_syswrap/syswrap-linux.c index 66e5ca62e..c81d941a7 100644 --- a/coregrind/m_syswrap/syswrap-linux.c +++ b/coregrind/m_syswrap/syswrap-linux.c @@ -13893,12 +13893,12 @@ PRE(sys_execveat) return; #endif + Int arg_1 = (Int) ARG1; const HChar *path = (const HChar*) ARG2; Addr arg_2 = ARG3; Addr arg_3 = ARG4; const HChar *buf; HChar *abs_path = NULL; - Bool check_at_symlink = False; Bool check_pathptr = True; if (ML_(safe_to_deref) (path, 1)) { @@ -13906,8 +13906,12 @@ PRE(sys_execveat) * and just pass the pathname, try to determine * the absolute path otherwise. */ if (path[0] != '/') { - /* Check dirfd is a valid fd. */ - if (!ML_(fd_allowed)(ARG1, "execveat", tid, False)) { + /* Check dirfd is a valid fd. + * BUT: allow special value of AT_FDCWD (-101) per the execveat(2) man page: + * If pathname is relative and dirfd is the special value AT_FDCWD, + * then pathname is interpreted relative to the current working directory + * of the calling process (like execve(2)). */ + if (arg_1 != VKI_AT_FDCWD && !ML_(fd_allowed)(arg_1, "execveat", tid, False)) { SET_STATUS_Failure( VKI_EBADF ); return; } @@ -13915,38 +13919,33 @@ PRE(sys_execveat) set then dirfd describes the whole path. */ if (path[0] == '\0') { if (ARG5 & VKI_AT_EMPTY_PATH) { - if (VG_(resolve_filename)(ARG1, &buf)) { + if (VG_(resolve_filename)(arg_1, &buf)) { path = buf; check_pathptr = False; } } - } - else if (ARG1 == VKI_AT_FDCWD) { - check_at_symlink = True; - } else - if (ARG5 & VKI_AT_SYMLINK_NOFOLLOW) - check_at_symlink = True; - else if (VG_(resolve_filename)(ARG1, &buf)) { + } else if (VG_(resolve_filename)(arg_1, &buf)) { abs_path = VG_(malloc)("execveat", (VG_(strlen)(buf) + 1 + VG_(strlen)(path) + 1)); VG_(sprintf)(abs_path, "%s/%s", buf, path); path = abs_path; check_pathptr = False; - } - else - path = NULL; - if (check_at_symlink) { - struct vg_stat statbuf; - SysRes statres; - - statres = VG_(stat)(path, &statbuf); - if (sr_isError(statres) || VKI_S_ISLNK(statbuf.mode)) { - SET_STATUS_Failure( VKI_ELOOP ); - return; - } } } + if (ARG5 & VKI_AT_SYMLINK_NOFOLLOW) { + struct vg_stat statbuf; + SysRes statres; + statres = VG_(stat)(path, &statbuf); + if (sr_isError(statres) || VKI_S_ISLNK(statbuf.mode)) { + SET_STATUS_Failure( VKI_ELOOP ); + return; + } + } + if(ARG5 & ~(VKI_AT_SYMLINK_NOFOLLOW | VKI_AT_EMPTY_PATH)) { + SET_STATUS_Failure( VKI_EINVAL ); + return; + } } else { SET_STATUS_Failure(VKI_EFAULT); return;