Re: [BUG] binfmt_elf: get_user() called in vma_dump_size() afterset_fs(KERNEL_DS)
From: Linus Torvalds
Date: Fri Feb 06 2009 - 16:57:54 EST
On Fri, 6 Feb 2009, Andrew Morton wrote:
> On Fri, 06 Feb 2009 18:10:35 +0100
> Gerald Schaefer <gerald.schaefer@xxxxxxxxxx> wrote:
> >
> > elf_core_dump() does a set_fs(KERNEL_DS) and then calls vma_dump_size(),
> > which uses get_user() to check for an ELF header at vma->vm_start in the
> > user mapping. This is a bug because vm_start is a user virtual address and
> > get_user() will fail or even read from a kernel address (KERNEL_DS).
> >
> > Maybe a get_user_pages() should be used to get the user data, or a temporary
> > set_fs(USER_DS)?
> >
>
> Could use __get_user() to skip the access_ok() check?
That's not the problem.
The problem is that
(a) some architectures actually use separate address spaces (sparc, at
least), so using "get_user()" while you are in KERNEL_DS will
literally access the wrong thing.
__get_user doesn't affect this part.
(b) the security one: KERNEL_DS will change the access checks that
get_user() does, and allow access to kernel memory.
Using __get_user _does_ affect this part by removing the checks, but
since the problem was that KERNEL_DS already _weakened_ the checks,
removign the too-weak checking doesn't actually help.
> We'd need to be sure that the address isn't a kernel address or iomem
> or something.
As mentioned, even this won't actually help. On at least some sparc
machines (sparc64?), memory accesses really are segmented, with different
address spaces for user and kernel mode, so a user and kernel address may
actually have the exact same pointer value, but point to different memory!
So you can't just check the address. You really have to do the whole
"set_fs()" thing to set the segment register.
Linus
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/