Signed-off-by: Kyle Huey <kh...@kylehuey.com> --- arch/x86/entry/syscalls/syscall_32.tbl | 1 + arch/x86/kernel/process.c | 80 ++++++++++++++++++++++++++++++++++ arch/x86/kernel/process_64.c | 66 ---------------------------- 3 files changed, 81 insertions(+), 66 deletions(-)
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index f848572..3b6965b 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -386,3 +386,4 @@ 377 i386 copy_file_range sys_copy_file_range 378 i386 preadv2 sys_preadv2 compat_sys_preadv2 379 i386 pwritev2 sys_pwritev2 compat_sys_pwritev2 +380 i386 arch_prctl sys_arch_prctl diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 62c0b0e..0f857c3 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -20,6 +20,7 @@ #include <linux/cpuidle.h> #include <trace/events/power.h> #include <linux/hw_breakpoint.h> +#include <linux/syscalls.h> #include <asm/cpu.h> #include <asm/apic.h> #include <asm/syscalls.h> @@ -32,6 +33,7 @@ #include <asm/tlbflush.h> #include <asm/mce.h> #include <asm/vm86.h> +#include <asm/prctl.h> /* * per-CPU TSS segments. Threads are completely 'soft' on Linux, @@ -567,3 +569,81 @@ unsigned long get_wchan(struct task_struct *p) } while (count++ < 16 && p->state != TASK_RUNNING); return 0; } + +long do_arch_prctl(struct task_struct *task, int code, unsigned long arg2) +{ + int ret = 0; + int doit = task == current; + int is_32 = IS_ENABLED(CONFIG_IA32_EMULATION) && test_thread_flag(TIF_IA32); + int cpu; + + switch (code) { +#ifdef CONFIG_X86_64 + case ARCH_SET_GS: + if (is_32) + return -EINVAL; + if (arg2 >= TASK_SIZE_MAX) + return -EPERM; + cpu = get_cpu(); + task->thread.gsindex = 0; + task->thread.gsbase = arg2; + if (doit) { + load_gs_index(0); + ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, arg2); + } + put_cpu(); + break; + case ARCH_SET_FS: + if (is_32) + return -EINVAL; + /* Not strictly needed for fs, but do it for symmetry + with gs */ + if (arg2 >= TASK_SIZE_MAX) + return -EPERM; + cpu = get_cpu(); + task->thread.fsindex = 0; + task->thread.fsbase = arg2; + if (doit) { + /* set the selector to 0 to not confuse __switch_to */ + loadsegment(fs, 0); + ret = wrmsrl_safe(MSR_FS_BASE, arg2); + } + put_cpu(); + break; + case ARCH_GET_FS: { + unsigned long base; + + if (is_32) + return -EINVAL; + if (doit) + rdmsrl(MSR_FS_BASE, base); + else + base = task->thread.fsbase; + ret = put_user(base, (unsigned long __user *)arg2); + break; + } + case ARCH_GET_GS: { + unsigned long base; + + if (is_32) + return -EINVAL; + if (doit) + rdmsrl(MSR_KERNEL_GS_BASE, base); + else + base = task->thread.gsbase; + ret = put_user(base, (unsigned long __user *)arg2); + break; + } +#endif + default: + ret = -EINVAL; + break; + } + + return ret; +} + +SYSCALL_DEFINE2(arch_prctl, int, code, unsigned long, arg2) +{ + return do_arch_prctl(current, code, arg2); +} diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 63236d8..e8c6302 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -524,72 +524,6 @@ void set_personality_ia32(bool x32) } EXPORT_SYMBOL_GPL(set_personality_ia32); -long do_arch_prctl(struct task_struct *task, int code, unsigned long addr) -{ - int ret = 0; - int doit = task == current; - int cpu; - - switch (code) { - case ARCH_SET_GS: - if (addr >= TASK_SIZE_MAX) - return -EPERM; - cpu = get_cpu(); - task->thread.gsindex = 0; - task->thread.gsbase = addr; - if (doit) { - load_gs_index(0); - ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, addr); - } - put_cpu(); - break; - case ARCH_SET_FS: - /* Not strictly needed for fs, but do it for symmetry - with gs */ - if (addr >= TASK_SIZE_MAX) - return -EPERM; - cpu = get_cpu(); - task->thread.fsindex = 0; - task->thread.fsbase = addr; - if (doit) { - /* set the selector to 0 to not confuse __switch_to */ - loadsegment(fs, 0); - ret = wrmsrl_safe(MSR_FS_BASE, addr); - } - put_cpu(); - break; - case ARCH_GET_FS: { - unsigned long base; - if (doit) - rdmsrl(MSR_FS_BASE, base); - else - base = task->thread.fsbase; - ret = put_user(base, (unsigned long __user *)addr); - break; - } - case ARCH_GET_GS: { - unsigned long base; - if (doit) - rdmsrl(MSR_KERNEL_GS_BASE, base); - else - base = task->thread.gsbase; - ret = put_user(base, (unsigned long __user *)addr); - break; - } - - default: - ret = -EINVAL; - break; - } - - return ret; -} - -long sys_arch_prctl(int code, unsigned long addr) -{ - return do_arch_prctl(current, code, addr); -} - unsigned long KSTK_ESP(struct task_struct *task) { return task_pt_regs(task)->sp; -- 2.7.4