x86/mm: Make in_compat_syscall() work during exec
authorDmitry Safonov <dsafonov@virtuozzo.com>
Fri, 31 Mar 2017 11:11:37 +0000 (14:11 +0300)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 31 Mar 2017 14:53:02 +0000 (16:53 +0200)
commitada26481dfe698ac64b4aaf19a726e66eb8508c6
tree8d2686f21d2cf410411cf4d34aaea8ae75f0f406
parent952a6c2c094f4eda295f20c42e6e2d73735950fa
x86/mm: Make in_compat_syscall() work during exec

The x86 mmap() code selects the mmap base for an allocation depending on
the bitness of the syscall. For 64bit sycalls it select mm->mmap_base and
for 32bit mm->mmap_compat_base.

On execve the registers of the task invoking exec() are copied to the child
pt_regs. So child->pt_regs->orig_ax contains the execve syscall number of the
parent.

exec() calls mmap() which in turn uses in_compat_syscall() to check whether
the mapping is for a 32bit or a 64bit task. The decision is made on the
following criteria:

  ia32   child->thread.status & TS_COMPAT
   x32   child->pt_regs.orig_ax & __X32_SYSCALL_BIT
  ia64   !ia32 && !x32

child->thread.status is corretly set up in set_personality_*(), but the
syscall number in child->pt_regs.orig_ax is left unmodified.

Therefore the parent/child combinations work or fail in the following way:

Parent Child Child->thread_status  child->pt_regs.orig_ax  in_compat()  Works
ia64    ia64   TS_COMPAT == 0    __X32_SYSCALL_BIT == 0     false       Y
ia64    ia32   TS_COMPAT == 1    __X32_SYSCALL_BIT == 0     true        Y
ia64     x32   TS_COMPAT == 0    __X32_SYSCALL_BIT == 0     false       N
ia32    ia64   TS_COMPAT == 0    __X32_SYSCALL_BIT == 0     false       Y
ia32    ia32   TS_COMPAT == 1    __X32_SYSCALL_BIT == 0     true        Y
ia32     x32   TS_COMPAT == 0    __X32_SYSCALL_BIT == 0     false       N
 x32    ia64   TS_COMPAT == 0    __X32_SYSCALL_BIT == 1     true        N
 x32    ia32   TS_COMPAT == 1    __X32_SYSCALL_BIT == 1     true        Y
 x32     x32   TS_COMPAT == 0    __X32_SYSCALL_BIT == 1     true        Y

Make set_personality_*() store the syscall number incl. __X32_SYSCALL_BIT
which corresponds to the newly started ELF executable in the childs
pt_regs, i.e. pretend that the exec was invoked from a task with the same
executable format.

So both thread.status and pt_regs.orig_ax correspond to the new ELF format
and in_compat_syscall() returns the correct result.

[ tglx: Rewrote changelog ]

Fixes: commit 1b028f784e8c ("x86/mm: Introduce mmap_compat_base() for 32-bit mmap()")
Reported-by: Adam Borowski <kilobyte@angband.pl>
Suggested-by: H. Peter Anvin <hpa@zytor.com>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
Cc: 0x7f454c46@gmail.com
Cc: linux-mm@kvack.org
Cc: Andrei Vagin <avagin@gmail.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Borislav Petkov <bp@suse.de>
Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Link: http://lkml.kernel.org/r/20170331111137.28170-1-dsafonov@virtuozzo.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/kernel/process_64.c