From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-pg0-f72.google.com (mail-pg0-f72.google.com [74.125.83.72]) by kanga.kvack.org (Postfix) with ESMTP id 475B56B0038 for ; Fri, 31 Mar 2017 07:15:25 -0400 (EDT) Received: by mail-pg0-f72.google.com with SMTP id u3so75118522pgn.12 for ; Fri, 31 Mar 2017 04:15:25 -0700 (PDT) Received: from EUR01-HE1-obe.outbound.protection.outlook.com (mail-he1eur01on0104.outbound.protection.outlook.com. [104.47.0.104]) by mx.google.com with ESMTPS id f1si4949995pld.13.2017.03.31.04.15.23 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 31 Mar 2017 04:15:24 -0700 (PDT) From: Dmitry Safonov Subject: [PATCHv5] x86/mm: make in_compat_syscall() work during exec Date: Fri, 31 Mar 2017 14:11:37 +0300 Message-ID: <20170331111137.28170-1-dsafonov@virtuozzo.com> MIME-Version: 1.0 Content-Type: text/plain Sender: owner-linux-mm@kvack.org List-ID: To: linux-kernel@vger.kernel.org Cc: 0x7f454c46@gmail.com, Dmitry Safonov , Adam Borowski , "H. Peter Anvin" , Thomas Gleixner , linux-mm@kvack.org, Andrei Vagin , Cyrill Gorcunov , Borislav Petkov , "Kirill A. Shutemov" , x86@kernel.org, Andy Lutomirski , Ingo Molnar After my changes to mmap(), its code now relies on the bitness of performing syscall. According to that, it chooses the base of allocation: mmap_base for 64-bit mmap() and mmap_compat_base for 32-bit syscall. It was done by: commit 1b028f784e8c ("x86/mm: Introduce mmap_compat_base() for 32-bit mmap()"). The code afterwards relies on in_compat_syscall() returning true for 32-bit syscalls. It's usually so while we're in context of application that does 32-bit syscalls. But during exec() it is not valid for x32 ELF. The reason is that the application hasn't yet done any syscall, so x32 bit has not being set. But do_execve() calls load_elf_binary(), which adds mappings with elf_map(). That results in -ENOMEM for x32 ELF binaries as in_compat_syscall() says we're in 64-bit syscall and so mmap_base is used instead of mmap_compat_base. For i386 ELFs it works as SET_PERSONALITY() sets TS_COMPAT flag. As suggested by HPA and with diff by Thomas, make SET_PERSONALITY() change original syscall number to appropriate execve() number to pretend that we've come from the same bitness syscall as loading binary. Fixes: commit 1b028f784e8c ("x86/mm: Introduce mmap_compat_base() for 32-bit mmap()") Cc: 0x7f454c46@gmail.com Cc: linux-mm@kvack.org Cc: Andrei Vagin Cc: Cyrill Gorcunov Cc: Borislav Petkov Cc: "Kirill A. Shutemov" Cc: x86@kernel.org Cc: Andy Lutomirski Cc: Ingo Molnar Reported-by: Adam Borowski Suggested-by: H. Peter Anvin Suggested-by: Thomas Gleixner Signed-off-by: Dmitry Safonov --- v5: Use generated unistd includes than defining syscall numbers by hands. v4: Pretend that we've come from appropriate system call than only setting/dropping x32 bit. arch/x86/kernel/process_64.c | 70 +++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index ea1a6180bf39..57a827f3ed5b 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -53,6 +53,13 @@ #include #include #include +#include +#ifdef CONFIG_X86_X32 +#include +#endif +#ifdef CONFIG_IA32_EMULATION +#include +#endif __visible DEFINE_PER_CPU(unsigned long, rsp_scratch); @@ -494,6 +501,8 @@ void set_personality_64bit(void) clear_thread_flag(TIF_IA32); clear_thread_flag(TIF_ADDR32); clear_thread_flag(TIF_X32); + /* Pretend that this comes from a 64bit execve */ + task_pt_regs(current)->orig_ax = __NR_execve; /* Ensure the corresponding mm is not marked. */ if (current->mm) @@ -506,32 +515,51 @@ void set_personality_64bit(void) current->personality &= ~READ_IMPLIES_EXEC; } -void set_personality_ia32(bool x32) +static void __set_personality_x32(void) { - /* inherit personality from parent */ +#ifdef CONFIG_X86_X32 + clear_thread_flag(TIF_IA32); + set_thread_flag(TIF_X32); + if (current->mm) + current->mm->context.ia32_compat = TIF_X32; + current->personality &= ~READ_IMPLIES_EXEC; + /* + * in_compat_syscall() uses the presence of the x32 + * syscall bit flag to determine compat status. + * The x86 mmap() code relies on the syscall bitness + * so set x32 syscall bit right here to make + * in_compat_syscall() work during exec(). + * + * Pretend to come from a x32 execve. + */ + task_pt_regs(current)->orig_ax = __NR_x32_execve | __X32_SYSCALL_BIT; + current->thread.status &= ~TS_COMPAT; +#endif +} +static void __set_personality_ia32(void) +{ +#ifdef CONFIG_IA32_EMULATION + set_thread_flag(TIF_IA32); + clear_thread_flag(TIF_X32); + if (current->mm) + current->mm->context.ia32_compat = TIF_IA32; + current->personality |= force_personality32; + /* Prepare the first "return" to user space */ + task_pt_regs(current)->orig_ax = __NR_ia32_execve; + current->thread.status |= TS_COMPAT; +#endif +} + +void set_personality_ia32(bool x32) +{ /* Make sure to be in 32bit mode */ set_thread_flag(TIF_ADDR32); - /* Mark the associated mm as containing 32-bit tasks. */ - if (x32) { - clear_thread_flag(TIF_IA32); - set_thread_flag(TIF_X32); - if (current->mm) - current->mm->context.ia32_compat = TIF_X32; - current->personality &= ~READ_IMPLIES_EXEC; - /* in_compat_syscall() uses the presence of the x32 - syscall bit flag to determine compat status */ - current->thread.status &= ~TS_COMPAT; - } else { - set_thread_flag(TIF_IA32); - clear_thread_flag(TIF_X32); - if (current->mm) - current->mm->context.ia32_compat = TIF_IA32; - current->personality |= force_personality32; - /* Prepare the first "return" to user space */ - current->thread.status |= TS_COMPAT; - } + if (x32) + __set_personality_x32(); + else + __set_personality_ia32(); } EXPORT_SYMBOL_GPL(set_personality_ia32); -- 2.12.0 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: email@kvack.org