[RHEL7,COMMIT] x86,ia32: Restore 32bit personality

Submitted by Konstantin Khorenko on July 20, 2018, 3:55 p.m.

Details

Message ID 201807201555.w6KFtAoP008049@finist_ce7.work
State New
Series "x86,ia32: Restore 32bit personality"
Headers show

Commit Message

Konstantin Khorenko July 20, 2018, 3:55 p.m.
The commit is pushed to "branch-rh7-3.10.0-862.9.1.vz7.70.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-862.9.1.vz7.63.2
------>
commit 11a262dc1502ffddbf28a2661d0308976e3539d5
Author: Cyrill Gorcunov <gorcunov@virtuozzo.com>
Date:   Fri Jul 20 18:55:10 2018 +0300

    x86,ia32: Restore 32bit personality
    
    When restoring compatible applications (ie running in ia32 mode) we have to
    restore thread flags and mm context at least otherwise compat_alloc_user_space
    may allocate values from old_rsp remembered at last 64 bit syscall leading to
    EFAULT.
    
    Note the vanilla kernel already switched to pt_regs::sp for this sake but
    backporting the patch is not an option due to its size.
    
    In the patch we simply make sure that the task is in container and has no
    TIF_IA32 before thus when CRIU call for rt_sigreturn via int80 gate we adjust
    the flags needed.
    
     | [root@pcs7 ~]# vzctl exec b3a8161f-50e2-4072-ab0a-93baa9ac20d4 cat /home/criu/test/zdtm/static/aio01.out
     | 15:08:15.525:   201: tail=2, head=128, nr=255
     | 15:08:26.424:   201: tail=2, head=128, nr=255
     | 15:08:26.425:   201: tail=3, head=128, nr=255
     | 15:08:26.425:   201: PASS
    
    Previously this test failed because after the restore we call for io_submit in
    the test and kernel tries to allocate helper structure to convert arguments
    from compat to native mode with compat_alloc_user_space and failed.
    
    https://jira.sw.ru/browse/PSBM-76965
    
    CC: Konstantin Khorenko <khorenko@virtuozzo.com>
    Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
---
 arch/x86/ia32/ia32_signal.c | 55 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

Patch hide | download patch | download mbox

diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index cbd5c5ad5edf..ef81b880e96a 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -35,6 +35,10 @@ 
 #include <asm/sys_ia32.h>
 #include <asm/smap.h>
 
+#ifdef CONFIG_VE
+# include <linux/ve.h>
+#endif
+
 void sigaction_compat_abi(struct k_sigaction *act, struct k_sigaction *oact)
 {
 	/* Don't leak in-kernel non-uapi flags to user-space */
@@ -258,6 +262,55 @@  asmlinkage long sys32_sigreturn(void)
 	return 0;
 }
 
+#ifdef CONFIG_VE
+extern int force_personality32;
+
+static void ve_set_personality_ia32(struct pt_regs *regs)
+{
+#ifdef CONFIG_IA32_EMULATION
+	if (ve_is_super(current->task_ve) ||
+	    test_thread_flag(TIF_IA32))
+		return;
+
+	/*
+	 * TIF_IA32 flag is used by the kernel to figure out
+	 * the task address space at least but more importantly
+	 * in compat_alloc_user_space when an application is
+	 * doing syscalls like io_submit and etc. So when
+	 * we restore such process via rt_sigreturn syscall
+	 * from inside of native mode with int80 gate help
+	 * we have to restore the TIF_IA32 and ia32_compat
+	 * at least.
+	 *
+	 * NOTE 1: this applies to applications running inside
+	 * VE only. The vanilla kernel already throwing off
+	 * old_rsp symbol but the patch is too big to merge
+	 * inside.
+	 *
+	 * NOTE 2: this is pretty valid to call int80 on
+	 * regular x86-64 bit application with rt_sigreturn
+	 * as syscall number, but it's rather an exception
+	 * than common practice (except CRIU of course).
+	 */
+	if (regs->cs == __USER32_CS) {
+		/*
+		 * It's close to set_personality_ia32
+		 * but we don't want to change orig_ax.
+		 */
+		set_thread_flag(TIF_ADDR32);
+		set_thread_flag(TIF_IA32);
+		clear_thread_flag(TIF_X32);
+		if (current->mm)
+			current->mm->context.ia32_compat = TIF_IA32;
+		current->personality |= force_personality32;
+		current_thread_info()->status |= TS_COMPAT;
+	}
+#endif
+}
+#else
+static void ve_set_personality_ia32(struct pt_regs *regs) { }
+#endif
+
 asmlinkage long sys32_rt_sigreturn(void)
 {
 	struct pt_regs *regs = current_pt_regs();
@@ -277,6 +330,8 @@  asmlinkage long sys32_rt_sigreturn(void)
 	if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
 		goto badframe;
 
+	ve_set_personality_ia32(regs);
+
 	if (compat_restore_altstack(&frame->uc.uc_stack))
 		goto badframe;