Messages in this thread | | | From | Jann Horn <> | Date | Fri, 14 Aug 2020 21:37:24 +0200 | Subject | Re: [PATCH] x86/fsgsbase/64: Fix NULL deref in 86_fsgsbase_read_task |
| |
On Fri, Aug 14, 2020 at 9:03 PM Andy Lutomirski <luto@amacapital.net> wrote: > > On Aug 14, 2020, at 11:16 AM, Eric Dumazet <edumazet@google.com> wrote: > > > > syzbot found its way in 86_fsgsbase_read_task() [1] > > > > Fix is to make sure ldt pointer is not NULL > > Acked-by: Andy Lutomirski <luto@kernel.org> > > Maybe add something like this to the changelog: > > This can happen if ptrace() or sigreturn() pokes an LDT selector into FS or GS for a task with no LDT and something tries to read the base before a return to usermode notices the bad selector and fixes it. > > I’ll see if I can whip up a test case too.
This is the reproducer I used to test this on 4.20.17:
#include <stdio.h> #include <unistd.h> #include <err.h> #include <sys/wait.h> #include <sys/ptrace.h> #include <sys/user.h> #include <sys/prctl.h> #include <signal.h> #include <stddef.h> #include <errno.h>
#define SEL_LDT 0x4 #define USER_RPL 0x3 #define LDT_SELECTOR(idx) (((idx)<<3) | SEL_LDT | USER_RPL)
int main(void) { pid_t child = fork(); if (child == -1) err(1, "fork"); if (child == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL); while (1) pause(); } if (ptrace(PTRACE_ATTACH, child, NULL, NULL)) err(1, "PTRACE_ATTACH"); int status; if (waitpid(child, &status, __WALL) != child) err(1, "waitpid"); if (ptrace( PTRACE_POKEUSER, child, (void*)offsetof(struct user_regs_struct, fs), (void*)LDT_SELECTOR(0) )) err(1, "PTRACE_POKEUSER"); errno = 0; unsigned long val = ptrace( PTRACE_PEEKUSER, child, (void*)offsetof(struct user_regs_struct, fs_base), NULL ); printf("PTRACE_PEEKUSER returns user_regs_struct.fs_base = 0x%lx (%m)\n", val); }
| |