From edec33dfec3da19bb3a992cbd1410fe7ab06d33a Mon Sep 17 00:00:00 2001 From: Akarsh Chaudhary Date: Tue, 1 Sep 2026 07:13:23 +0000 Subject: [PATCH 1/4] [High] Patch sudo for CVE-2026-82474. --- SPECS/sudo/CVE-2026-82474.patch | 554 ++++++++++++++++++++++++++++++++ SPECS/sudo/sudo.spec | 6 +- 2 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 SPECS/sudo/CVE-2026-82474.patch diff --git a/SPECS/sudo/CVE-2026-82474.patch b/SPECS/sudo/CVE-2026-82474.patch new file mode 100644 index 00000000000..aae0793c531 --- /dev/null +++ b/SPECS/sudo/CVE-2026-82474.patch @@ -0,0 +1,554 @@ +From 71fbe42dcd5a1c8f799540583a2dfb2ae6221edf Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Fri, 20 Mar 2026 06:48:32 -0600 +Subject: [PATCH] Add intercept and log_subcmds support for execveat(2). + +Upstream-reference: https://github.com/sudo-project/sudo/commit/71fbe42dcd5a1c8f799540583a2dfb2ae6221edf.patch +--- + src/exec_ptrace.c | 169 +++++++++++++++++++++++++++++++++++++++++++----------- + src/exec_ptrace.h | 54 +++++++++++++---- + 2 files changed, 180 insertions(+), 43 deletions(-) + +diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c +index f970c4f..711f6e3 100644 +--- a/src/exec_ptrace.c ++++ b/src/exec_ptrace.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2022 Todd C. Miller ++ * Copyright (c) 2022, 2026 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above +@@ -114,7 +114,7 @@ set_syscallno(pid_t pid, struct sudo_ptrace_regs *regs, int syscallno) + } + + static inline unsigned long +-get_sc_arg1(struct sudo_ptrace_regs *regs) ++get_sc_arg1(pid_t pid, struct sudo_ptrace_regs *regs) + { + if (regs->compat) { + return compat_reg_arg1(regs->u.compat); +@@ -134,7 +134,7 @@ set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) + } + + static inline unsigned long +-get_sc_arg2(struct sudo_ptrace_regs *regs) ++get_sc_arg2(pid_t pid, struct sudo_ptrace_regs *regs) + { + if (regs->compat) { + return compat_reg_arg2(regs->u.compat); +@@ -154,7 +154,7 @@ set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) + } + + static inline unsigned long +-get_sc_arg3(struct sudo_ptrace_regs *regs) ++get_sc_arg3(pid_t pid, struct sudo_ptrace_regs *regs) + { + if (regs->compat) { + return compat_reg_arg3(regs->u.compat); +@@ -163,7 +163,6 @@ get_sc_arg3(struct sudo_ptrace_regs *regs) + } + } + +-# ifdef notyet + static inline void + set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) + { +@@ -175,7 +174,7 @@ set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) + } + + static inline unsigned long +-get_sc_arg4(struct sudo_ptrace_regs *regs) ++get_sc_arg4(pid_t pid, struct sudo_ptrace_regs *regs) + { + if (regs->compat) { + return compat_reg_arg4(regs->u.compat); +@@ -184,8 +183,9 @@ get_sc_arg4(struct sudo_ptrace_regs *regs) + } + } + ++# ifdef notyet + static inline void +-set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) ++set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) + { + if (regs->compat) { + compat_reg_set_arg4(regs->u.compat, addr); +@@ -195,6 +195,28 @@ set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) + } + # endif /* notyet */ + ++static inline unsigned long ++get_sc_arg5(pid_t pid, struct sudo_ptrace_regs *regs) ++{ ++ if (regs->compat) { ++ return compat_reg_arg5(regs->u.compat); ++ } else { ++ return reg_arg5(regs->u.native); ++ } ++} ++ ++# ifdef notyet ++static inline void ++set_sc_arg5(struct sudo_ptrace_regs *regs, unsigned long addr) ++{ ++ if (regs->compat) { ++ compat_reg_set_arg5(regs->u.compat, addr); ++ } else { ++ reg_set_arg5(regs->u.native, addr); ++ } ++} ++# endif /* notyet */ ++ + # else /* SECCOMP_AUDIT_ARCH_COMPAT */ + + static inline unsigned long +@@ -222,7 +244,7 @@ set_syscallno(pid_t pid, struct sudo_ptrace_regs *regs, int syscallno) + } + + static inline unsigned long +-get_sc_arg1(struct sudo_ptrace_regs *regs) ++get_sc_arg1(pid_t pid, struct sudo_ptrace_regs *regs) + { + return reg_arg1(regs->u.native); + } +@@ -234,7 +256,7 @@ set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) + } + + static inline unsigned long +-get_sc_arg2(struct sudo_ptrace_regs *regs) ++get_sc_arg2(pid_t pid, struct sudo_ptrace_regs *regs) + { + return reg_arg2(regs->u.native); + } +@@ -246,12 +268,11 @@ set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) + } + + static inline unsigned long +-get_sc_arg3(struct sudo_ptrace_regs *regs) ++get_sc_arg3(pid_t pid, struct sudo_ptrace_regs *regs) + { + return reg_arg3(regs->u.native); + } + +-# ifdef notyet + static inline void + set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) + { +@@ -259,17 +280,33 @@ set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) + } + + static inline unsigned long +-get_sc_arg4(struct sudo_ptrace_regs *regs) ++get_sc_arg4(pid_t pid, struct sudo_ptrace_regs *regs) + { + return reg_arg4(regs->u.native); + } + ++# ifdef notyet + static inline void + set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) + { + reg_set_arg4(regs->u.native, addr); + } + # endif /* notyet */ ++ ++static inline unsigned long ++get_sc_arg5(pid_t pid, struct sudo_ptrace_regs *regs) ++{ ++ return reg_arg5(regs->u.native); ++} ++ ++# ifdef notyet ++static inline void ++set_sc_arg5(struct sudo_ptrace_regs *regs, unsigned long addr) ++{ ++ reg_set_arg5(regs->u.native, addr); ++} ++# endif /* notyet */ ++ + # endif /* SECCOMP_AUDIT_ARCH_COMPAT */ + + /* +@@ -357,7 +394,7 @@ ptrace_readv_string(pid_t pid, unsigned long addr, char *buf, size_t bufsize) + const char *cp, *buf0 = buf; + struct iovec local, remote; + ssize_t nread; +- debug_decl(ptrace_read_string, SUDO_DEBUG_EXEC); ++ debug_decl(ptrace_readv_string, SUDO_DEBUG_EXEC); + + /* + * Read the string via process_vm_readv(2) one page at a time. +@@ -969,15 +1006,16 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) + * Returns a dynamically allocated buffer the parent is responsible for. + */ + static char * +-get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +- int *argc_out, char ***argv_out, int *envc_out, char ***envp_out) ++get_exec_info(pid_t pid, bool is_execveat, struct sudo_ptrace_regs *regs, ++ char **pathname_out, int *argc_out, char ***argv_out, int *envc_out, ++ char ***envp_out) + { + char *argbuf, **argv, **envp, *pathname = NULL; + unsigned long argv_addr, envp_addr, path_addr; + size_t bufsize, off = 0; +- int i, argc, envc = 0; ++ int i, argc, dirfd = -1, flags = 0, envc = 0; + ssize_t nread; +- debug_decl(get_execve_info, SUDO_DEBUG_EXEC); ++ debug_decl(get_exec_info, SUDO_DEBUG_EXEC); + + bufsize = PATH_MAX + arg_max; + argbuf = malloc(bufsize); +@@ -986,13 +1024,22 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, + goto bad; + } + +- /* execve(2) takes three arguments: pathname, argv, envp. */ +- path_addr = get_sc_arg1(regs); +- argv_addr = get_sc_arg2(regs); +- envp_addr = get_sc_arg3(regs); ++ if (is_execveat) { ++ /* execveat(2) takes five arguments */ ++ dirfd = get_sc_arg1(pid, regs) & 0xffffffff; ++ path_addr = get_sc_arg2(pid, regs); ++ argv_addr = get_sc_arg3(pid, regs); ++ envp_addr = get_sc_arg4(pid, regs); ++ flags = get_sc_arg5(pid, regs) & 0xffffffff; ++ } else { ++ /* execve(2) takes three arguments */ ++ path_addr = get_sc_arg1(pid, regs); ++ argv_addr = get_sc_arg2(pid, regs); ++ envp_addr = get_sc_arg3(pid, regs); ++ } + sudo_debug_printf(SUDO_DEBUG_INFO, +- "%s: %d: path 0x%lx, argv 0x%lx, envp 0x%lx", __func__, +- (int)pid, path_addr, argv_addr, envp_addr); ++ "%s: %d: dirfd %d, path 0x%lx, argv 0x%lx, envp 0x%lx, flags: 0x%x", ++ __func__, (int)pid, dirfd, path_addr, argv_addr, envp_addr, flags); + + /* Read the pathname, if not NULL. */ + if (path_addr != 0) { +@@ -1003,6 +1050,52 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, + "unable to read execve pathname for process %d", (int)pid); + goto bad; + } ++ ++ if (is_execveat && argbuf[0] != '/') { ++ char name[64]; ++ ++ if (argbuf[0] == '\0' && ISSET(flags, AT_EMPTY_PATH)) { ++ /* Used to implement fexecve(3) */ ++ (void)snprintf(name, sizeof(name), "fd/%d", dirfd); ++ if (!proc_read_link(pid, name, argbuf, bufsize)) { ++ sudo_debug_printf( ++ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, ++ "unable to read link /proc/%d/fd/%d", (int)pid, dirfd); ++ goto bad; ++ } ++ nread = (ssize_t)strlen(argbuf) + 1; ++ } else { ++ /* Relative path, resolve dirfd to a path. */ ++ char dir[PATH_MAX]; ++ size_t dirlen, pathlen; ++ ++ if (dirfd == AT_FDCWD) { ++ (void)strlcpy(name, "cwd", sizeof(name)); ++ } else { ++ (void)snprintf(name, sizeof(name), "fd/%d", dirfd); ++ } ++ if (!proc_read_link(pid, name, dir, sizeof(dir))) { ++ sudo_debug_printf( ++ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, ++ "unable to read link /proc/%d/%s", (int)pid, name); ++ goto bad; ++ } ++ dirlen = strlen(dir); ++ pathlen = strlen(argbuf); ++ if (dirlen + 1 + pathlen + 1 > bufsize) { ++ sudo_debug_printf( ++ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, ++ "out of space storing path for process %d", (int)pid); ++ goto bad; ++ } ++ /* Prepend directory to relative pathname. */ ++ memmove(argbuf + dirlen + 1, argbuf, pathlen + 1); ++ memcpy(argbuf, dir, dirlen); ++ argbuf[dirlen] = '/'; ++ nread = (ssize_t)(dirlen + 1 + pathlen + 1); ++ } ++ } ++ + /* Defer setting pathname until after all reallocations are done. */ + off = (size_t)nread; + } +@@ -1013,7 +1106,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, + if (nread == -1) { + sudo_debug_printf( + SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, +- "unable to read execve argv for process %d", (int)pid); ++ "unable to read argv for process %d", (int)pid); + goto bad; + } + off += (size_t)nread; +@@ -1033,7 +1126,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, + if (nread == -1) { + sudo_debug_printf( + SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, +- "unable to read execve envp for process %d", (int)pid); ++ "unable to read envp for process %d", (int)pid); + goto bad; + } + +@@ -1573,6 +1666,7 @@ check_argv: + + /* + * Verify that the execve(2) argument we wrote match the contents of closure. ++ * TODO: test execveat(2) too. + * Returns true if they match, else false. + */ + static bool +@@ -1584,7 +1678,7 @@ verify_execve_args(pid_t pid, struct sudo_ptrace_regs *regs, + bool ret = false; + debug_decl(verify_execve_args, SUDO_DEBUG_EXEC); + +- buf = get_execve_info(pid, regs, &pathname, &argc, &argv, ++ buf = get_exec_info(pid, false, regs, &pathname, &argc, &argv, + &envc, &envp); + if (buf != NULL) { + ret = execve_args_match(pathname, argc, argv, envc, envp, false, closure); +@@ -1711,6 +1805,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + bool path_mismatch = false; + bool argv_mismatch = false; + char cwd[PATH_MAX], *orig_argv0; ++ bool is_execveat = false; + unsigned long msg; + bool ret = false; + int i, oldcwd = -1; +@@ -1745,8 +1840,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + /* Handled below. */ + break; + case COMPAT_execveat: +- /* We don't currently check execveat(2). */ +- debug_return_bool(true); ++ /* Handled below. */ ++ is_execveat = true; + break; + default: + sudo_warnx("%s: unexpected compat system call %d", +@@ -1768,8 +1863,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + case X32_execveat: + # endif + case __NR_execveat: +- /* We don't currently check execveat(2). */ +- debug_return_bool(true); ++ /* Handled below. */ ++ is_execveat = true; + break; + default: + sudo_warnx("%s: unexpected system call %d", __func__, syscallno); +@@ -1780,7 +1875,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + /* Get the current working directory and execve info. */ + if (!proc_read_link(pid, "cwd", cwd, sizeof(cwd))) + (void)strlcpy(cwd, "unknown", sizeof(cwd)); +- buf = get_execve_info(pid, ®s, &pathname, &argc, &argv, ++ buf = get_exec_info(pid, is_execveat, ®s, &pathname, &argc, &argv, + &envc, &envp); + if (buf == NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, +@@ -1904,7 +1999,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + + if (argv_mismatch) { + /* Update argv address in the tracee to our new value. */ +- set_sc_arg2(®s, sp); ++ if (is_execveat) { ++ set_sc_arg3(®s, sp); ++ } else { ++ set_sc_arg2(®s, sp); ++ } + + /* Skip over argv pointers (plus NULL) for string table. */ + strtab += ((size_t)argc + 1 + regs.compat) * regs.wordsize; +@@ -1917,7 +2016,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + } + if (path_mismatch) { + /* Update pathname address in the tracee to our new value. */ +- set_sc_arg1(®s, strtab); ++ if (is_execveat) { ++ set_sc_arg2(®s, strtab); ++ } else { ++ set_sc_arg1(®s, strtab); ++ } + + /* Write pathname to the string table. */ + nwritten = ptrace_write_string(pid, strtab, closure->command); +diff --git a/src/exec_ptrace.h b/src/exec_ptrace.h +index 2da6233..6e86d65 100644 +--- a/src/exec_ptrace.h ++++ b/src/exec_ptrace.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2022 Todd C. Miller ++ * Copyright (c) 2022-2026 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above +@@ -87,6 +87,7 @@ + # define reg_arg2(x) (x).rsi + # define reg_arg3(x) (x).rdx + # define reg_arg4(x) (x).r10 ++# define reg_arg5(x) (x).r8 + #elif defined(__aarch64__) + # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64 + # define sudo_pt_regs struct user_pt_regs +@@ -97,6 +98,7 @@ + # define reg_arg2(x) (x).regs[1] /* x1 */ + # define reg_arg3(x) (x).regs[2] /* x2 */ + # define reg_arg4(x) (x).regs[3] /* x3 */ ++# define reg_arg5(x) (x).regs[4] /* x4 */ + # define reg_set_syscall(_r, _nr) do { \ + struct iovec _iov; \ + long _syscallno = (_nr); \ +@@ -115,6 +117,7 @@ + # define reg_arg2(x) (x).ARM_r1 + # define reg_arg3(x) (x).ARM_r2 + # define reg_arg4(x) (x).ARM_r3 ++# define reg_arg5(x) (x).ARM_r4 + # define reg_set_syscall(_r, _nr) do { \ + ptrace(PTRACE_SET_SYSCALL, pid, NULL, _nr); \ + } while (0) +@@ -129,6 +132,7 @@ + # define reg_arg2(x) (x).gr[25] /* r25 */ + # define reg_arg3(x) (x).gr[24] /* r24 */ + # define reg_arg4(x) (x).gr[23] /* r23 */ ++# define reg_arg5(x) (x).gr[22] /* r22 */ + #elif defined(__i386__) + # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386 + # define sudo_pt_regs struct user_regs_struct +@@ -139,6 +143,7 @@ + # define reg_arg2(x) (x).ecx + # define reg_arg3(x) (x).edx + # define reg_arg4(x) (x).esi ++# define reg_arg5(x) (x).edi + #elif defined(__mips__) + # if _MIPS_SIM == _MIPS_SIM_ABI32 + # /* Linux o32 style syscalls, 4000-4999. */ +@@ -204,15 +209,16 @@ + else \ + (_r).regs[6] = _v; /* a2 */ \ + } while (0) +-/* XXX - reg_arg4 probably wrong for syscall() type calls on 032. */ ++/* ++ * The o32 ABI uses registers a0 - a3 for the first 4 args, all others ++ * are on the stack. We do not implement reg_set_arg4 or reg_set_arg5. ++ */ + # define reg_arg4(x) \ +- ((x).regs[2] == __NR_O32_Linux ? (x).regs[8] : (x).regs[7]) +-# define reg_set_arg4(_r, _v) do { \ +- if ((_r).regs[2] == __NR_O32_Linux) \ +- (_r).regs[8] = _v; /* a4 */ \ +- else \ +- (_r).regs[7] = _v; /* a3 */ \ +-} while (0) ++ ((x).regs[2] == __NR_O32_Linux ? \ ++ (ptrace(PTRACE_PEEKDATA, pid, reg_sp(x) + 16, NULL)) : (x).regs[7]) ++# define reg_arg5(x) \ ++ ptrace(PTRACE_PEEKDATA, pid, reg_sp(x) + \ ++ ((x).regs[2] == __NR_O32_Linux ? 20 : 16)) + # define reg_set_syscall(_r, _nr) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[4] = _nr; /* a0 */ \ +@@ -237,6 +243,7 @@ + # define reg_arg2(x) (x).gpr[4] /* r4 */ + # define reg_arg3(x) (x).gpr[5] /* r5 */ + # define reg_arg4(x) (x).gpr[6] /* r6 */ ++# define reg_arg5(x) (x).gpr[7] /* r7 */ + # define reg_set_retval(_r, _v) do { \ + if (((_r).trap & 0xfff0) == 0x3000) { \ + /* scv 0 system call, uses negative error code for result. */ \ +@@ -260,6 +267,7 @@ + # define reg_arg2(x) (x).a1 + # define reg_arg3(x) (x).a2 + # define reg_arg4(x) (x).a3 ++# define reg_arg5(x) (x).a4 + #elif defined(__s390__) + /* + * Both the syscall number and return value are stored in r2 for +@@ -277,7 +285,8 @@ + # define reg_arg1(x) (x).orig_gpr2 /* r2 */ + # define reg_arg2(x) (x).gprs[3] /* r3 */ + # define reg_arg3(x) (x).gprs[4] /* r4 */ +-# define reg_arg4(x) (x).gprs[5] /* r6 */ ++# define reg_arg4(x) (x).gprs[5] /* r5 */ ++# define reg_arg5(x) (x).gprs[6] /* r6 */ + #else + # error "Do not know how to find your architecture's registers" + #endif +@@ -322,6 +331,7 @@ struct i386_user_regs_struct { + # define compat_reg_arg2(x) (x).ecx + # define compat_reg_arg3(x) (x).edx + # define compat_reg_arg4(x) (x).esi ++# define compat_reg_arg5(x) (x).edi + #elif defined(__aarch64__) + struct arm_pt_regs { + unsigned int uregs[18]; +@@ -337,6 +347,7 @@ struct arm_pt_regs { + # define compat_reg_arg2(x) (x).uregs[1] /* r1 */ + # define compat_reg_arg3(x) (x).uregs[2] /* r2 */ + # define compat_reg_arg4(x) (x).uregs[3] /* r3 */ ++# define compat_reg_arg5(x) (x).uregs[4] /* r4 */ + # define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr) + #elif defined(__mips__) + # if _MIPS_SIM == _MIPS_SIM_ABI64 +@@ -374,6 +385,8 @@ struct arm_pt_regs { + # define compat_reg_set_arg3(_r, _v) reg_set_arg3(_r, _v) + # define compat_reg_arg4(x) reg_arg4(x) + # define compat_reg_set_arg4(_r, _v) reg_set_arg4(_r, _v) ++# define compat_reg_arg5(x) reg_arg5(x) ++# define compat_reg_set_arg5(_r, _v) reg_set_arg5(_r, _v) + # define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr) + #elif defined(__powerpc64__) + struct ppc_pt_regs { +@@ -407,6 +420,7 @@ struct ppc_pt_regs { + # define compat_reg_arg2(x) (x).gpr[4] /* r4 */ + # define compat_reg_arg3(x) (x).gpr[5] /* r5 */ + # define compat_reg_arg4(x) (x).gpr[6] /* r6 */ ++# define compat_reg_arg5(x) (x).gpr[7] /* r7 */ + # define compat_reg_set_retval(_r, _v) reg_set_retval(_r, _v) + #endif + +@@ -475,6 +489,16 @@ struct ppc_pt_regs { + compat_reg_arg4(_r) = (_v); \ + } while (0) + #endif ++#ifndef reg_set_arg5 ++# define reg_set_arg5(_r, _v) do { \ ++ reg_arg5(_r) = (_v); \ ++} while (0) ++#endif ++#ifndef compat_reg_set_arg5 ++# define compat_reg_set_arg5(_r, _v) do { \ ++ compat_reg_arg5(_r) = (_v); \ ++} while (0) ++#endif + + /* Set the syscall arguments the "normal" way by default. */ + #ifndef reg_set_arg1 +@@ -517,6 +541,16 @@ struct ppc_pt_regs { + compat_reg_arg4(_r) = (_v); \ + } while (0) + #endif ++#ifndef reg_set_arg5 ++# define reg_set_arg5(_r, _v) do { \ ++ reg_arg5(_r) = (_v); \ ++} while (0) ++#endif ++#ifndef compat_reg_set_arg5 ++# define compat_reg_set_arg5(_r, _v) do { \ ++ compat_reg_arg5(_r) = (_v); \ ++} while (0) ++#endif + + struct sudo_ptrace_regs { + union { diff --git a/SPECS/sudo/sudo.spec b/SPECS/sudo/sudo.spec index 8cd306eb642..bff09053c23 100644 --- a/SPECS/sudo/sudo.spec +++ b/SPECS/sudo/sudo.spec @@ -1,7 +1,7 @@ Summary: Sudo Name: sudo Version: 1.9.17 -Release: 2%{?dist} +Release: 3%{?dist} License: ISC URL: https://www.sudo.ws/ Group: System Environment/Security @@ -11,6 +11,7 @@ Source0: https://www.sudo.ws/sudo/dist/%{name}-%{version}.tar.gz Patch0: CVE-2025-32462.patch Patch1: CVE-2025-32463.patch Patch2: CVE-2026-35535.patch +Patch3: CVE-2026-82474.patch BuildRequires: audit-devel BuildRequires: man-db BuildRequires: openssl-devel @@ -103,6 +104,9 @@ fi %exclude /etc/sudoers.dist %changelog +* Tue Sep 01 2026 Akarsh Chaudhary - 1.9.17-3 +- Patch for CVE-2026-82474 + * Thu Apr 09 2026 Azure Linux Security Servicing Account - 1.9.17-2 - Patch for CVE-2026-35535 From 678bc0d5897af0164b1c026bc733b93d73957207 Mon Sep 17 00:00:00 2001 From: Akarsh Chaudhary Date: Tue, 1 Sep 2026 08:36:38 +0000 Subject: [PATCH 2/4] Adding missed comments in the patch. --- SPECS/sudo/CVE-2026-82474.patch | 36 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/SPECS/sudo/CVE-2026-82474.patch b/SPECS/sudo/CVE-2026-82474.patch index aae0793c531..75c1bafd79d 100644 --- a/SPECS/sudo/CVE-2026-82474.patch +++ b/SPECS/sudo/CVE-2026-82474.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Add intercept and log_subcmds support for execveat(2). Upstream-reference: https://github.com/sudo-project/sudo/commit/71fbe42dcd5a1c8f799540583a2dfb2ae6221edf.patch --- - src/exec_ptrace.c | 169 +++++++++++++++++++++++++++++++++++++++++++----------- + src/exec_ptrace.c | 172 +++++++++++++++++++++++++++++++++++++++++++----------- src/exec_ptrace.h | 54 +++++++++++++---- - 2 files changed, 180 insertions(+), 43 deletions(-) + 2 files changed, 182 insertions(+), 44 deletions(-) diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c -index f970c4f..711f6e3 100644 +index f970c4f..cf9e973 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -1,5 +1,5 @@ @@ -180,7 +180,12 @@ index f970c4f..711f6e3 100644 /* * Read the string via process_vm_readv(2) one page at a time. -@@ -969,15 +1006,16 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) +@@ -965,19 +1002,20 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) + } + + /* +- * Read the filename, argv and envp of the execve(2) system call. ++ * Read execve(2) or execveat(2) system call arguments from pid. * Returns a dynamically allocated buffer the parent is responsible for. */ static char * @@ -230,11 +235,12 @@ index f970c4f..711f6e3 100644 /* Read the pathname, if not NULL. */ if (path_addr != 0) { -@@ -1003,6 +1050,52 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +@@ -1003,6 +1050,53 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, "unable to read execve pathname for process %d", (int)pid); goto bad; } + ++ /* Handle relative path and AT_EMPTY_PATH for execveat() */ + if (is_execveat && argbuf[0] != '/') { + char name[64]; + @@ -283,7 +289,7 @@ index f970c4f..711f6e3 100644 /* Defer setting pathname until after all reallocations are done. */ off = (size_t)nread; } -@@ -1013,7 +1106,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +@@ -1013,7 +1107,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, if (nread == -1) { sudo_debug_printf( SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, @@ -292,7 +298,7 @@ index f970c4f..711f6e3 100644 goto bad; } off += (size_t)nread; -@@ -1033,7 +1126,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +@@ -1033,7 +1127,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, if (nread == -1) { sudo_debug_printf( SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, @@ -301,7 +307,7 @@ index f970c4f..711f6e3 100644 goto bad; } -@@ -1573,6 +1666,7 @@ check_argv: +@@ -1573,6 +1667,7 @@ check_argv: /* * Verify that the execve(2) argument we wrote match the contents of closure. @@ -309,7 +315,7 @@ index f970c4f..711f6e3 100644 * Returns true if they match, else false. */ static bool -@@ -1584,7 +1678,7 @@ verify_execve_args(pid_t pid, struct sudo_ptrace_regs *regs, +@@ -1584,7 +1679,7 @@ verify_execve_args(pid_t pid, struct sudo_ptrace_regs *regs, bool ret = false; debug_decl(verify_execve_args, SUDO_DEBUG_EXEC); @@ -318,7 +324,7 @@ index f970c4f..711f6e3 100644 &envc, &envp); if (buf != NULL) { ret = execve_args_match(pathname, argc, argv, envc, envp, false, closure); -@@ -1711,6 +1805,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1711,6 +1806,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) bool path_mismatch = false; bool argv_mismatch = false; char cwd[PATH_MAX], *orig_argv0; @@ -326,7 +332,7 @@ index f970c4f..711f6e3 100644 unsigned long msg; bool ret = false; int i, oldcwd = -1; -@@ -1745,8 +1840,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1745,8 +1841,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) /* Handled below. */ break; case COMPAT_execveat: @@ -337,7 +343,7 @@ index f970c4f..711f6e3 100644 break; default: sudo_warnx("%s: unexpected compat system call %d", -@@ -1768,8 +1863,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1768,8 +1864,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) case X32_execveat: # endif case __NR_execveat: @@ -348,7 +354,7 @@ index f970c4f..711f6e3 100644 break; default: sudo_warnx("%s: unexpected system call %d", __func__, syscallno); -@@ -1780,7 +1875,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1780,7 +1876,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) /* Get the current working directory and execve info. */ if (!proc_read_link(pid, "cwd", cwd, sizeof(cwd))) (void)strlcpy(cwd, "unknown", sizeof(cwd)); @@ -357,7 +363,7 @@ index f970c4f..711f6e3 100644 &envc, &envp); if (buf == NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, -@@ -1904,7 +1999,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1904,7 +2000,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) if (argv_mismatch) { /* Update argv address in the tracee to our new value. */ @@ -370,7 +376,7 @@ index f970c4f..711f6e3 100644 /* Skip over argv pointers (plus NULL) for string table. */ strtab += ((size_t)argc + 1 + regs.compat) * regs.wordsize; -@@ -1917,7 +2016,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1917,7 +2017,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) } if (path_mismatch) { /* Update pathname address in the tracee to our new value. */ From f89aa3712550b953b01b4fec73a0eb0959c9a012 Mon Sep 17 00:00:00 2001 From: Akarsh Chaudhary Date: Wed, 2 Sep 2026 05:03:30 +0000 Subject: [PATCH 3/4] sudo: refactor CVE-2026-82474 patch to stay in sync with upstream source --- SPECS/sudo/CVE-2026-82474.patch | 141 ++++++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 33 deletions(-) diff --git a/SPECS/sudo/CVE-2026-82474.patch b/SPECS/sudo/CVE-2026-82474.patch index 75c1bafd79d..da7b990e1e1 100644 --- a/SPECS/sudo/CVE-2026-82474.patch +++ b/SPECS/sudo/CVE-2026-82474.patch @@ -3,14 +3,28 @@ From: "Todd C. Miller" Date: Fri, 20 Mar 2026 06:48:32 -0600 Subject: [PATCH] Add intercept and log_subcmds support for execveat(2). +Previously, sudo would always allow execveat(2) in intercept mode +to support sudo's own use of fexecve(3) for the "fdexec" setting. +Since sudoers is path-based, we must use /proc to resolve the file +descriptor in execveat(2) to a path. + +Credit: + - XlabAI Team of Tencent Xuanwu Lab (xlabai@tencent.com) + - Atuin Automated Vulnerability Discovery Engine + - Guannan Wang, Zhanpeng Liu, Guancheng Li + +Also reported by: + - Nofil Qasim + - Quentin Chalabi + Upstream-reference: https://github.com/sudo-project/sudo/commit/71fbe42dcd5a1c8f799540583a2dfb2ae6221edf.patch --- - src/exec_ptrace.c | 172 +++++++++++++++++++++++++++++++++++++++++++----------- - src/exec_ptrace.h | 54 +++++++++++++---- - 2 files changed, 182 insertions(+), 44 deletions(-) + src/exec_ptrace.c | 208 ++++++++++++++++++++++++++++++++++++---------- + src/exec_ptrace.h | 54 +++++++++--- + 2 files changed, 210 insertions(+), 52 deletions(-) diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c -index f970c4f..cf9e973 100644 +index f970c4f..f718dba 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -1,5 +1,5 @@ @@ -180,7 +194,34 @@ index f970c4f..cf9e973 100644 /* * Read the string via process_vm_readv(2) one page at a time. -@@ -965,19 +1002,20 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) +@@ -943,9 +980,11 @@ ptrace_write_vec(pid_t pid, struct sudo_ptrace_regs *regs, char **vec, + /* + * Read a link from /proc/PID and store the result in buf. + * Used to read the cwd and exe links in /proc/PID. +- * Returns true on success, else false. ++ * Returns the length of the link on success, else -1. ++ * NUL-terminates buf on success (not included in length). ++ * Note: name and buf _may_ overlap. + */ +-static bool ++static ssize_t + proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) + { + ssize_t len; +@@ -955,29 +994,30 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) + len = snprintf(path, sizeof(path), "/proc/%d/%s", (int)pid, name); + if (len > 0 && len < ssizeof(path)) { + len = readlink(path, buf, bufsize - 1); +- if (len != -1) { ++ if (len != -1 && (size_t)len != bufsize) { + /* readlink(2) does not add the NUL for us. */ + buf[len] = '\0'; +- debug_return_bool(true); ++ debug_return_ssize_t(len); + } + } +- debug_return_bool(false); ++ debug_return_ssize_t(-1); } /* @@ -206,7 +247,7 @@ index f970c4f..cf9e973 100644 bufsize = PATH_MAX + arg_max; argbuf = malloc(bufsize); -@@ -986,13 +1024,22 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +@@ -986,13 +1026,22 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, goto bad; } @@ -235,61 +276,83 @@ index f970c4f..cf9e973 100644 /* Read the pathname, if not NULL. */ if (path_addr != 0) { -@@ -1003,6 +1050,53 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, - "unable to read execve pathname for process %d", (int)pid); +@@ -1000,9 +1049,74 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, + if (nread == -1) { + sudo_debug_printf( + SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, +- "unable to read execve pathname for process %d", (int)pid); ++ "unable to read execveat pathname for process %d", (int)pid); goto bad; } + + /* Handle relative path and AT_EMPTY_PATH for execveat() */ + if (is_execveat && argbuf[0] != '/') { -+ char name[64]; -+ + if (argbuf[0] == '\0' && ISSET(flags, AT_EMPTY_PATH)) { + /* Used to implement fexecve(3) */ ++ char name[64]; + (void)snprintf(name, sizeof(name), "fd/%d", dirfd); -+ if (!proc_read_link(pid, name, argbuf, bufsize)) { ++ ssize_t len = proc_read_link(pid, name, argbuf, bufsize); ++ if (len == -1) { + sudo_debug_printf( + SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "unable to read link /proc/%d/fd/%d", (int)pid, dirfd); + goto bad; + } -+ nread = (ssize_t)strlen(argbuf) + 1; ++ nread = len + 1; + } else { + /* Relative path, resolve dirfd to a path. */ + char dir[PATH_MAX]; -+ size_t dirlen, pathlen; ++ char name[64]; + + if (dirfd == AT_FDCWD) { + (void)strlcpy(name, "cwd", sizeof(name)); + } else { + (void)snprintf(name, sizeof(name), "fd/%d", dirfd); + } -+ if (!proc_read_link(pid, name, dir, sizeof(dir))) { ++ ssize_t len = proc_read_link(pid, name, dir, sizeof(dir)); ++ if (len == -1) { + sudo_debug_printf( + SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "unable to read link /proc/%d/%s", (int)pid, name); + goto bad; + } -+ dirlen = strlen(dir); -+ pathlen = strlen(argbuf); -+ if (dirlen + 1 + pathlen + 1 > bufsize) { ++ if ((size_t)(nread + len + 1) >= bufsize) { + sudo_debug_printf( + SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, -+ "out of space storing path for process %d", (int)pid); ++ "out of space storing path for process %d", ++ (int)pid); + goto bad; + } + /* Prepend directory to relative pathname. */ -+ memmove(argbuf + dirlen + 1, argbuf, pathlen + 1); -+ memcpy(argbuf, dir, dirlen); -+ argbuf[dirlen] = '/'; -+ nread = (ssize_t)(dirlen + 1 + pathlen + 1); ++ memmove(argbuf + len + 1, argbuf, len + 1); ++ memcpy(argbuf, dir, len); ++ argbuf[len] = '/'; ++ nread += len + 1; ++ } ++ } ++ ++ /* ++ * For fexecve() the path may be in the form /proc/self/fd/N ++ * Used by glibc when the system doesn't support execveat(2). ++ */ ++ if (!ISSET(flags, AT_SYMLINK_NOFOLLOW) && ++ strncmp(argbuf, "/proc/self/fd/", 14) == 0) { ++ const char *fdstr = argbuf + 14; ++ const char *name = argbuf + 11; ++ const char *errstr; ++ (void)sudo_strtonum(fdstr, 0, INT_MAX, &errstr); ++ if (errstr == NULL) { ++ /* Rewrite argbuf with link target (if it is one). */ ++ ssize_t len = proc_read_link(pid, name, argbuf, bufsize); ++ if (len != -1) ++ nread = len + 1; + } + } + /* Defer setting pathname until after all reallocations are done. */ off = (size_t)nread; } -@@ -1013,7 +1107,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +@@ -1013,7 +1127,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, if (nread == -1) { sudo_debug_printf( SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, @@ -298,7 +361,7 @@ index f970c4f..cf9e973 100644 goto bad; } off += (size_t)nread; -@@ -1033,7 +1127,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, +@@ -1033,7 +1147,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, if (nread == -1) { sudo_debug_printf( SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, @@ -307,7 +370,7 @@ index f970c4f..cf9e973 100644 goto bad; } -@@ -1573,6 +1667,7 @@ check_argv: +@@ -1573,6 +1687,7 @@ check_argv: /* * Verify that the execve(2) argument we wrote match the contents of closure. @@ -315,7 +378,7 @@ index f970c4f..cf9e973 100644 * Returns true if they match, else false. */ static bool -@@ -1584,7 +1679,7 @@ verify_execve_args(pid_t pid, struct sudo_ptrace_regs *regs, +@@ -1584,7 +1699,7 @@ verify_execve_args(pid_t pid, struct sudo_ptrace_regs *regs, bool ret = false; debug_decl(verify_execve_args, SUDO_DEBUG_EXEC); @@ -324,7 +387,16 @@ index f970c4f..cf9e973 100644 &envc, &envp); if (buf != NULL) { ret = execve_args_match(pathname, argc, argv, envc, envp, false, closure); -@@ -1711,6 +1806,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1636,7 +1751,7 @@ ptrace_verify_post_exec(pid_t pid, struct sudo_ptrace_regs *regs, + } + + /* Get the executable path. */ +- if (!proc_read_link(pid, "exe", pathname, sizeof(pathname))) { ++ if (proc_read_link(pid, "exe", pathname, sizeof(pathname)) == -1) { + /* Missing /proc file system is not a fatal error. */ + sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: unable to read /proc/%d/exe", + __func__, (int)pid); +@@ -1711,6 +1826,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) bool path_mismatch = false; bool argv_mismatch = false; char cwd[PATH_MAX], *orig_argv0; @@ -332,7 +404,7 @@ index f970c4f..cf9e973 100644 unsigned long msg; bool ret = false; int i, oldcwd = -1; -@@ -1745,8 +1841,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1745,8 +1861,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) /* Handled below. */ break; case COMPAT_execveat: @@ -343,7 +415,7 @@ index f970c4f..cf9e973 100644 break; default: sudo_warnx("%s: unexpected compat system call %d", -@@ -1768,8 +1864,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1768,8 +1884,8 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) case X32_execveat: # endif case __NR_execveat: @@ -354,16 +426,19 @@ index f970c4f..cf9e973 100644 break; default: sudo_warnx("%s: unexpected system call %d", __func__, syscallno); -@@ -1780,7 +1876,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1778,9 +1894,9 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) + } + /* Get the current working directory and execve info. */ - if (!proc_read_link(pid, "cwd", cwd, sizeof(cwd))) +- if (!proc_read_link(pid, "cwd", cwd, sizeof(cwd))) ++ if (proc_read_link(pid, "cwd", cwd, sizeof(cwd)) == -1) (void)strlcpy(cwd, "unknown", sizeof(cwd)); - buf = get_execve_info(pid, ®s, &pathname, &argc, &argv, + buf = get_exec_info(pid, is_execveat, ®s, &pathname, &argc, &argv, &envc, &envp); if (buf == NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, -@@ -1904,7 +2000,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1904,7 +2020,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) if (argv_mismatch) { /* Update argv address in the tracee to our new value. */ @@ -376,7 +451,7 @@ index f970c4f..cf9e973 100644 /* Skip over argv pointers (plus NULL) for string table. */ strtab += ((size_t)argc + 1 + regs.compat) * regs.wordsize; -@@ -1917,7 +2017,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) +@@ -1917,7 +2037,11 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure) } if (path_mismatch) { /* Update pathname address in the tracee to our new value. */ From d2dd6666efc252d1dd3b0af44e45e86bb8a291bf Mon Sep 17 00:00:00 2001 From: Akarsh Chaudhary Date: Wed, 2 Sep 2026 05:45:29 +0000 Subject: [PATCH 4/4] sync proc_read_link readlink size with upstream (bufsize, not bufsize-1) --- SPECS/sudo/CVE-2026-82474.patch | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/SPECS/sudo/CVE-2026-82474.patch b/SPECS/sudo/CVE-2026-82474.patch index da7b990e1e1..351b09c0df0 100644 --- a/SPECS/sudo/CVE-2026-82474.patch +++ b/SPECS/sudo/CVE-2026-82474.patch @@ -19,12 +19,12 @@ Also reported by: Upstream-reference: https://github.com/sudo-project/sudo/commit/71fbe42dcd5a1c8f799540583a2dfb2ae6221edf.patch --- - src/exec_ptrace.c | 208 ++++++++++++++++++++++++++++++++++++---------- + src/exec_ptrace.c | 210 ++++++++++++++++++++++++++++++++++++---------- src/exec_ptrace.h | 54 +++++++++--- - 2 files changed, 210 insertions(+), 52 deletions(-) + 2 files changed, 211 insertions(+), 53 deletions(-) diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c -index f970c4f..f718dba 100644 +index f970c4f..0fb5750 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -1,5 +1,5 @@ @@ -208,11 +208,13 @@ index f970c4f..f718dba 100644 proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) { ssize_t len; -@@ -955,29 +994,30 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) +@@ -954,30 +993,31 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize) + len = snprintf(path, sizeof(path), "/proc/%d/%s", (int)pid, name); if (len > 0 && len < ssizeof(path)) { - len = readlink(path, buf, bufsize - 1); +- len = readlink(path, buf, bufsize - 1); - if (len != -1) { ++ len = readlink(path, buf, bufsize); + if (len != -1 && (size_t)len != bufsize) { /* readlink(2) does not add the NUL for us. */ buf[len] = '\0';