[prev in list] [next in list] [prev in thread] [next in thread]
List: oss-security
Subject: [oss-security] CVE-2022-32981: Linux kernel for powerpc 32-bit, buffer overflow in ptrace PEEKUSER/P
From: Michael Ellerman <mpe () ellerman ! id ! au>
Date: 2022-06-14 3:05:08
Message-ID: 87bkuwc5cr.fsf () mpe ! ellerman ! id ! au
[Download RAW message or body]
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;
}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic