Commit ff170cd0 authored by Gabriel Krisman Bertazi's avatar Gabriel Krisman Bertazi Committed by Thomas Gleixner

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: default avatarGabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: Andy Lutomirski<luto@kernel.org>
Link: https://lore.kernel.org/r/20201004032536.1229030-10-krisman@collabora.com
parent 3316ec8c
...@@ -316,7 +316,7 @@ static struct vm_area_struct gate_vma __ro_after_init = { ...@@ -316,7 +316,7 @@ static struct vm_area_struct gate_vma __ro_after_init = {
struct vm_area_struct *get_gate_vma(struct mm_struct *mm) struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
{ {
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
if (!mm || mm->context.ia32_compat) if (!mm || !(mm->context.flags & MM_CONTEXT_HAS_VSYSCALL))
return NULL; return NULL;
#endif #endif
if (vsyscall_mode == NONE) if (vsyscall_mode == NONE)
......
...@@ -6,6 +6,12 @@ ...@@ -6,6 +6,12 @@
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/bits.h>
/* Uprobes on this MM assume 32-bit code */
#define MM_CONTEXT_UPROBE_IA32 BIT(0)
/* vsyscall page is accessible on this MM */
#define MM_CONTEXT_HAS_VSYSCALL BIT(1)
/* /*
* x86 has arch-specific MMU state beyond what lives in mm_struct. * x86 has arch-specific MMU state beyond what lives in mm_struct.
...@@ -33,8 +39,7 @@ typedef struct { ...@@ -33,8 +39,7 @@ typedef struct {
#endif #endif
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
/* True if mm supports a task running in 32 bit compatibility mode. */ unsigned short flags;
unsigned short ia32_compat;
#endif #endif
struct mutex lock; struct mutex lock;
......
...@@ -177,7 +177,7 @@ static inline void arch_exit_mmap(struct mm_struct *mm) ...@@ -177,7 +177,7 @@ static inline void arch_exit_mmap(struct mm_struct *mm)
static inline bool is_64bit_mm(struct mm_struct *mm) static inline bool is_64bit_mm(struct mm_struct *mm)
{ {
return !IS_ENABLED(CONFIG_IA32_EMULATION) || return !IS_ENABLED(CONFIG_IA32_EMULATION) ||
!(mm->context.ia32_compat == TIF_IA32); !(mm->context.flags & MM_CONTEXT_UPROBE_IA32);
} }
#else #else
static inline bool is_64bit_mm(struct mm_struct *mm) static inline bool is_64bit_mm(struct mm_struct *mm)
......
...@@ -646,10 +646,8 @@ void set_personality_64bit(void) ...@@ -646,10 +646,8 @@ void set_personality_64bit(void)
/* Pretend that this comes from a 64bit execve */ /* Pretend that this comes from a 64bit execve */
task_pt_regs(current)->orig_ax = __NR_execve; task_pt_regs(current)->orig_ax = __NR_execve;
current_thread_info()->status &= ~TS_COMPAT; current_thread_info()->status &= ~TS_COMPAT;
/* Ensure the corresponding mm is not marked. */
if (current->mm) if (current->mm)
current->mm->context.ia32_compat = 0; current->mm->context.flags = MM_CONTEXT_HAS_VSYSCALL;
/* TBD: overwrites user setup. Should have two bits. /* TBD: overwrites user setup. Should have two bits.
But 64bit processes have always behaved this way, But 64bit processes have always behaved this way,
...@@ -664,7 +662,8 @@ static void __set_personality_x32(void) ...@@ -664,7 +662,8 @@ static void __set_personality_x32(void)
clear_thread_flag(TIF_IA32); clear_thread_flag(TIF_IA32);
set_thread_flag(TIF_X32); set_thread_flag(TIF_X32);
if (current->mm) if (current->mm)
current->mm->context.ia32_compat = TIF_X32; current->mm->context.flags = 0;
current->personality &= ~READ_IMPLIES_EXEC; current->personality &= ~READ_IMPLIES_EXEC;
/* /*
* in_32bit_syscall() uses the presence of the x32 syscall bit * in_32bit_syscall() uses the presence of the x32 syscall bit
...@@ -684,8 +683,14 @@ static void __set_personality_ia32(void) ...@@ -684,8 +683,14 @@ static void __set_personality_ia32(void)
#ifdef CONFIG_IA32_EMULATION #ifdef CONFIG_IA32_EMULATION
set_thread_flag(TIF_IA32); set_thread_flag(TIF_IA32);
clear_thread_flag(TIF_X32); clear_thread_flag(TIF_X32);
if (current->mm) if (current->mm) {
current->mm->context.ia32_compat = TIF_IA32; /*
* uprobes applied to this MM need to know this and
* cannot use user_64bit_mode() at that time.
*/
current->mm->context.flags = MM_CONTEXT_UPROBE_IA32;
}
current->personality |= force_personality32; current->personality |= force_personality32;
/* Prepare the first "return" to user space */ /* Prepare the first "return" to user space */
task_pt_regs(current)->orig_ax = __NR_ia32_execve; task_pt_regs(current)->orig_ax = __NR_ia32_execve;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment