Re: [syzbot] BUG: unable to handle kernel paging request in get_desc

From: Sean Christopherson
Date: Fri Nov 04 2022 - 15:39:30 EST


On Fri, Nov 04, 2022, Dmitry Vyukov wrote:
> On Fri, 4 Nov 2022 at 11:41, Sean Christopherson <seanjc@xxxxxxxxxx> wrote:
> >
> > On Fri, Nov 04, 2022, Dmitry Vyukov wrote:
> > > On Fri, 4 Nov 2022 at 10:39, 'Sean Christopherson' via syzkaller-bugs
> > > <syzkaller-bugs@xxxxxxxxxxxxxxxx> wrote:
> > > Can it be out-of-bounds or something?
> >
> > The lookup is on CS.base (I trimmed the stack in my first reply) as part of the
> > IOPL emulation to see if userspace is attempting CLI or STI, so it's not related
> > to the sigframe.
> >
> > insn_get_seg_base arch/x86/lib/insn-eval.c:725 [inline]
> > insn_get_effective_ip+0x187/0x1f0 arch/x86/lib/insn-eval.c:1476
> > fixup_iopl_exception+0xd0/0x190 arch/x86/kernel/traps.c:627
> > __exc_general_protection arch/x86/kernel/traps.c:752 [inline]
> > exc_general_protection+0x176/0x210 arch/x86/kernel/traps.c:728
> > asm_exc_general_protection+0x22/0x30 arch/x86/include/asm/idtentry.h:564
> > RIP: 0003:0x7f250f3abf8c
> >
> > It does look like some form out out-of-bounds selector though. The offset in the
> > splat suggests CS.sel is something way above __USER_CS, which would explain why
> > insn_get_effective_ip() is doing a lookup in the first place (CS.base is assumed
> > to be 0 if userspace is in 64-bit mode, user_64bit_mode() is true if CS == __USER_CS)),
> > I just can't figure out how that tiny reproducer is getting a bad CS. And the above
> > RIP strongly suggests userspace is indeed in 64-bit mode.
>
> My understanding is that rt_sigreturn() restores complete user context
> from the info stored on the stack.
> Normally signal delivery will store that info on the stack first. But
> in this case there is no signal delivery, so rt_sigreturn() reads
> complete garbage from the stack and restores it into the context. I
> assume this can setup any non-sense CS and maybe even pretend this is
> not normal x86_64 mode (?).

Ha! Indeed, shoving a sigcontext onto the stack that's valid enough to pass
basic checks but throws in a bad CS does the trick.

int main(void)
{
struct sigcontext regs;

syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
syscall(__NR_iopl, 3);

memset(&regs, 0, sizeof(regs));
regs.cs = 0x1d0;
syscall(__NR_rt_sigreturn);
return 0;
}

Same root cause, different fix. I'll post officially in a bit.

diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c
index dff9001e5e12..4a6440461c10 100644
--- a/arch/x86/mm/cpu_entry_area.c
+++ b/arch/x86/mm/cpu_entry_area.c
@@ -195,7 +195,7 @@ static void __init setup_cpu_entry_area(unsigned int cpu)
pgprot_t tss_prot = PAGE_KERNEL;
#endif

- cea_set_pte(&cea->gdt, get_cpu_gdt_paddr(cpu), gdt_prot);
+ cea_map_percpu_pages(&cea->gdt, get_cpu_gdt_rw(cpu), 1, gdt_prot);

cea_map_percpu_pages(&cea->entry_stack_page,
per_cpu_ptr(&entry_stack_storage, cpu), 1,

The other bare use of cea_set_pte() in percpu_setup_debug_store() also appears
suspect. The base debug_store area is mapped, but the buffers are not.