Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/mm: Convert mmu context ia32_compat into a proper flags field

The ia32_compat attribute is a weird thing. It mirrors TIF_IA32 and
TIF_X32 and is used only in two very unrelated places: (1) to decide if
the vsyscall page is accessible (2) for uprobes to find whether the
patched instruction is 32 or 64 bit.

In preparation to remove the TIF flags, a new mechanism is required for
ia32_compat, but given its odd semantics, adding a real flags field which
configures these specific behaviours is the best option.

So, set_personality_x64() can ask for the vsyscall page, which is not
available in x32/ia32 and set_personality_ia32() can configure the uprobe
code as needed.

uprobe cannot rely on other methods like user_64bit_mode() to decide how
to patch, so it needs some specific flag like this.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Andy Lutomirski<luto@kernel.org>
Link: https://lore.kernel.org/r/20201004032536.1229030-10-krisman@collabora.com

authored by

Gabriel Krisman Bertazi and committed by
Thomas Gleixner
ff170cd0 3316ec8c

+20 -10
+1 -1
arch/x86/entry/vsyscall/vsyscall_64.c
··· 316 316 struct vm_area_struct *get_gate_vma(struct mm_struct *mm) 317 317 { 318 318 #ifdef CONFIG_COMPAT 319 - if (!mm || mm->context.ia32_compat) 319 + if (!mm || !(mm->context.flags & MM_CONTEXT_HAS_VSYSCALL)) 320 320 return NULL; 321 321 #endif 322 322 if (vsyscall_mode == NONE)
+7 -2
arch/x86/include/asm/mmu.h
··· 6 6 #include <linux/rwsem.h> 7 7 #include <linux/mutex.h> 8 8 #include <linux/atomic.h> 9 + #include <linux/bits.h> 10 + 11 + /* Uprobes on this MM assume 32-bit code */ 12 + #define MM_CONTEXT_UPROBE_IA32 BIT(0) 13 + /* vsyscall page is accessible on this MM */ 14 + #define MM_CONTEXT_HAS_VSYSCALL BIT(1) 9 15 10 16 /* 11 17 * x86 has arch-specific MMU state beyond what lives in mm_struct. ··· 39 33 #endif 40 34 41 35 #ifdef CONFIG_X86_64 42 - /* True if mm supports a task running in 32 bit compatibility mode. */ 43 - unsigned short ia32_compat; 36 + unsigned short flags; 44 37 #endif 45 38 46 39 struct mutex lock;
+1 -1
arch/x86/include/asm/mmu_context.h
··· 177 177 static inline bool is_64bit_mm(struct mm_struct *mm) 178 178 { 179 179 return !IS_ENABLED(CONFIG_IA32_EMULATION) || 180 - !(mm->context.ia32_compat == TIF_IA32); 180 + !(mm->context.flags & MM_CONTEXT_UPROBE_IA32); 181 181 } 182 182 #else 183 183 static inline bool is_64bit_mm(struct mm_struct *mm)
+11 -6
arch/x86/kernel/process_64.c
··· 646 646 /* Pretend that this comes from a 64bit execve */ 647 647 task_pt_regs(current)->orig_ax = __NR_execve; 648 648 current_thread_info()->status &= ~TS_COMPAT; 649 - 650 - /* Ensure the corresponding mm is not marked. */ 651 649 if (current->mm) 652 - current->mm->context.ia32_compat = 0; 650 + current->mm->context.flags = MM_CONTEXT_HAS_VSYSCALL; 653 651 654 652 /* TBD: overwrites user setup. Should have two bits. 655 653 But 64bit processes have always behaved this way, ··· 662 664 clear_thread_flag(TIF_IA32); 663 665 set_thread_flag(TIF_X32); 664 666 if (current->mm) 665 - current->mm->context.ia32_compat = TIF_X32; 667 + current->mm->context.flags = 0; 668 + 666 669 current->personality &= ~READ_IMPLIES_EXEC; 667 670 /* 668 671 * in_32bit_syscall() uses the presence of the x32 syscall bit ··· 683 684 #ifdef CONFIG_IA32_EMULATION 684 685 set_thread_flag(TIF_IA32); 685 686 clear_thread_flag(TIF_X32); 686 - if (current->mm) 687 - current->mm->context.ia32_compat = TIF_IA32; 687 + if (current->mm) { 688 + /* 689 + * uprobes applied to this MM need to know this and 690 + * cannot use user_64bit_mode() at that time. 691 + */ 692 + current->mm->context.flags = MM_CONTEXT_UPROBE_IA32; 693 + } 694 + 688 695 current->personality |= force_personality32; 689 696 /* Prepare the first "return" to user space */ 690 697 task_pt_regs(current)->orig_ax = __NR_ia32_execve;