diff --git a/SPECS/sudo/CVE-2026-82474.patch b/SPECS/sudo/CVE-2026-82474.patch new file mode 100644 index 00000000000..351b09c0df0 --- /dev/null +++ b/SPECS/sudo/CVE-2026-82474.patch @@ -0,0 +1,637 @@ +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). + +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 | 210 ++++++++++++++++++++++++++++++++++++---------- + src/exec_ptrace.h | 54 +++++++++--- + 2 files changed, 211 insertions(+), 53 deletions(-) + +diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c +index f970c4f..0fb5750 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. +@@ -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; +@@ -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); +- 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'; +- debug_return_bool(true); ++ debug_return_ssize_t(len); + } + } +- debug_return_bool(false); ++ debug_return_ssize_t(-1); + } + + /* +- * 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 * +-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 +1026,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) { +@@ -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] != '/') { ++ 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); ++ 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 = len + 1; ++ } else { ++ /* Relative path, resolve dirfd to a path. */ ++ char dir[PATH_MAX]; ++ char name[64]; ++ ++ if (dirfd == AT_FDCWD) { ++ (void)strlcpy(name, "cwd", sizeof(name)); ++ } else { ++ (void)snprintf(name, sizeof(name), "fd/%d", dirfd); ++ } ++ 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; ++ } ++ 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); ++ goto bad; ++ } ++ /* Prepend directory to relative pathname. */ ++ 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 +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, +- "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 +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, +- "unable to read execve envp for process %d", (int)pid); ++ "unable to read envp for process %d", (int)pid); + goto bad; + } + +@@ -1573,6 +1687,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 +1699,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); +@@ -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; ++ bool is_execveat = false; + unsigned long msg; + bool ret = false; + int i, oldcwd = -1; +@@ -1745,8 +1861,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 +1884,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); +@@ -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)) == -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 +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. */ +- 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 +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. */ +- 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