oss-sec mailing list archives
CVE-2022-32981: Linux kernel for powerpc 32-bit, buffer overflow in ptrace PEEKUSER/POKEUSER
From: Michael Ellerman <mpe () ellerman id au>
Date: Tue, 14 Jun 2022 13:05:08 +1000
The Linux kernel for powerpc 32-bit has a buffer overflow in the handling of ptrace PEEKUSER/POKEUSER when accessing floating point registers. The fix for mainline is: https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/commit/?id=8e1278444446fc97778a5e5c99bca1ce0bbc5ec9 Which is included in v5.19-rc2. Stable backports have been posted. A test case is included below, it will report if the system is correctly patched, it is safe to run on an unpatched system. cheers --- #undef NDEBUG #include <assert.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/syscall.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> static double expected = 0.123456L; static int child(int shm_id) { int *cptr = shmat(shm_id, NULL, 0); asm volatile ( "lfd %%f0, 0(%0) ;" "lfd %%f1, 0(%0) ;" "li %%r9, 1 ;" "stw %%r9, 0(%1) ;" "1:" "lwz %%r9, 0(%2) ;" "cmpwi %%r9, 0 ;" "beq 1b ;" : // outputs : // inputs "b" (&expected), "b" (&cptr[1]), "b" (&cptr[0]) : // clobbers "memory", "r9", "fr0", "fr1" ); return 0; } int start_trace(pid_t child) { int ret; ret = ptrace(PTRACE_ATTACH, child, NULL, NULL); if (ret) { perror("ptrace(PTRACE_ATTACH) failed"); return -1; } ret = waitpid(child, NULL, 0); if (ret != child) { perror("waitpid() failed"); return -1; } return 0; } int stop_trace(pid_t child) { int ret; ret = ptrace(PTRACE_DETACH, child, NULL, NULL); if (ret) { perror("ptrace(PTRACE_DETACH) failed"); return -1; } return 0; } long raw_ptrace(enum __ptrace_request request, pid_t pid, unsigned long addr, void *data) { return syscall(__NR_ptrace, request, pid, (void *)addr, data); } #define PEEKS_PER_FPR (sizeof(__u64) / sizeof(unsigned long)) int peek_fpr(pid_t child, int frnum, __u64 *fpr) { unsigned long *p, addr; int i, fpindex; long ret; fpindex = PEEKS_PER_FPR * frnum; p = (unsigned long *)fpr; for (i = 0; i < PEEKS_PER_FPR; i++, p++) { addr = sizeof(unsigned long) * (PT_FPR0 + fpindex + i); ret = raw_ptrace(PTRACE_PEEKUSER, child, addr, p); if (ret) { perror("ptrace(PTRACE_PEEKUSR) failed"); return -1; } } return 0; } int parent(pid_t child) { double f0, f1; assert(start_trace(child) == 0); assert(peek_fpr(child, 0, (__u64 *)&f0) == 0); assert(peek_fpr(child, 1, (__u64 *)&f1) == 0); assert(stop_trace(child) == 0); printf("expected = %e\n", f0); printf("f0 = %e\n", f0); printf("f1 = %e\n", f1); if (f0 != expected || f1 != expected) { printf("FAIL - values don't match! Kernel is buggy.\n"); return -1; } printf("OK - values match\n"); return 0; } int main(void) { int shm_id, ret, status, *pptr; pid_t pid; shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); assert(shm_id != -1); pid = fork(); assert(pid >= 0); if (pid == 0) exit(child(shm_id)); pptr = shmat(shm_id, NULL, 0); // Wait for child to signal us to continue while (!pptr[1]) asm volatile("" : : : "memory"); ret = parent(pid); if (ret) { kill(pid, SIGTERM); shmdt((void *)pptr); shmctl(shm_id, IPC_RMID, NULL); return -1; } // Signal child to exit pptr[0] = 1; shmdt((void *)pptr); ret = wait(&status); shmctl(shm_id, IPC_RMID, NULL); assert(ret != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); return 0; }
Current thread:
- CVE-2022-32981: Linux kernel for powerpc 32-bit, buffer overflow in ptrace PEEKUSER/POKEUSER Michael Ellerman (Jun 14)