The goal of this set of patches is to change vm86 support to return to
userspace with the normal exit paths instead of leaving data on the kernel
stack and jumping directly into the exit asm routines. This fixes issues
like ptrace and syscall auditing not working with vm86, and makes possible
cleanups in the syscall exit work code.
Changes from v1:
- Added first two patches
- Changed userspace access to copy each field explicitly instead of relying
on the same order of members in the structure.
In-Reply-To:
There is no need to save FS and non-lazy GS outside the 32-bit regs. Lazy GS
still needs to be saved because it wasn't saved on syscall entry.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/include/asm/processor.h | 3 ++-
arch/x86/kernel/vm86_32.c | 6 ++----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 43e6519..a4b5c6a 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -410,9 +410,10 @@ struct thread_struct {
unsigned long v86flags;
unsigned long v86mask;
unsigned long saved_sp0;
- unsigned int saved_fs;
+#ifdef CONFIG_X86_32_LAZY_GS
unsigned int saved_gs;
#endif
+#endif
/* IO permissions: */
unsigned long *io_bitmap_ptr;
unsigned long iopl;
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index fc9db6e..75eab83 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -159,8 +159,7 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
ret = KVM86->regs32;
- ret->fs = current->thread.saved_fs;
- set_user_gs(ret, current->thread.saved_gs);
+ lazy_load_gs(current->thread.saved_gs);
return ret;
}
@@ -315,8 +314,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
*/
info->regs32->ax = VM86_SIGNAL;
tsk->thread.saved_sp0 = tsk->thread.sp0;
- tsk->thread.saved_fs = info->regs32->fs;
- tsk->thread.saved_gs = get_user_gs(info->regs32);
+ lazy_save_gs(tsk->thread.saved_gs);
tss = &per_cpu(cpu_tss, get_cpu());
tsk->thread.sp0 = (unsigned long) &info->VM86_TSS_ESP0;
--
2.4.3
There is no legitimate reason for usermode to modify the orig_ax field on
entry to vm86 mode, so copy it from the 32-bit regs.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/kernel/vm86_32.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 75eab83..8424290 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -294,6 +294,8 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
info->regs.pt.flags |= info->regs32->flags & ~SAFE_MASK;
info->regs.pt.flags |= X86_VM_MASK;
+ info->regs.pt.orig_ax = info->regs32->orig_ax;
+
switch (info->cpu_type) {
case CPU_286:
tsk->thread.v86mask = 0;
--
2.4.3
Move the userspace accesses down into the common function in
preparation for the next set of patches.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/include/asm/processor.h | 2 +-
arch/x86/kernel/vm86_32.c | 176 +++++++++++++++++++--------------------
2 files changed, 89 insertions(+), 89 deletions(-)
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index a4b5c6a..98190a6 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -405,7 +405,7 @@ struct thread_struct {
unsigned long error_code;
#ifdef CONFIG_X86_32
/* Virtual 86 mode info */
- struct vm86_struct __user *vm86_info;
+ struct vm86plus_struct __user *vm86_info;
unsigned long screen_bitmap;
unsigned long v86flags;
unsigned long v86mask;
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 8424290..08bbb69 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -90,46 +90,13 @@
#define SAFE_MASK (0xDD5)
#define RETURN_MASK (0xDFF)
-/* convert kernel_vm86_regs to vm86_regs */
-static int copy_vm86_regs_to_user(struct vm86_regs __user *user,
- const struct kernel_vm86_regs *regs)
-{
- int ret = 0;
-
- /*
- * kernel_vm86_regs is missing gs, so copy everything up to
- * (but not including) orig_eax, and then rest including orig_eax.
- */
- ret += copy_to_user(user, regs, offsetof(struct kernel_vm86_regs, pt.orig_ax));
- ret += copy_to_user(&user->orig_eax, ®s->pt.orig_ax,
- sizeof(struct kernel_vm86_regs) -
- offsetof(struct kernel_vm86_regs, pt.orig_ax));
-
- return ret;
-}
-
-/* convert vm86_regs to kernel_vm86_regs */
-static int copy_vm86_regs_from_user(struct kernel_vm86_regs *regs,
- const struct vm86_regs __user *user,
- unsigned extra)
-{
- int ret = 0;
-
- /* copy ax-fs inclusive */
- ret += copy_from_user(regs, user, offsetof(struct kernel_vm86_regs, pt.orig_ax));
- /* copy orig_ax-__gsh+extra */
- ret += copy_from_user(®s->pt.orig_ax, &user->orig_eax,
- sizeof(struct kernel_vm86_regs) -
- offsetof(struct kernel_vm86_regs, pt.orig_ax) +
- extra);
- return ret;
-}
-
struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
{
struct tss_struct *tss;
struct pt_regs *ret;
- unsigned long tmp;
+ struct task_struct *tsk = current;
+ struct vm86plus_struct __user *user;
+ long err = 0;
/*
* This gets called from entry.S with interrupts disabled, but
@@ -138,23 +105,42 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
*/
local_irq_enable();
- if (!current->thread.vm86_info) {
+ if (!tsk->thread.vm86_info) {
pr_alert("no vm86_info: BAD\n");
do_exit(SIGSEGV);
}
- set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | current->thread.v86mask);
- tmp = copy_vm86_regs_to_user(¤t->thread.vm86_info->regs, regs);
- tmp += put_user(current->thread.screen_bitmap, ¤t->thread.vm86_info->screen_bitmap);
- if (tmp) {
+ set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | tsk->thread.v86mask);
+ user = tsk->thread.vm86_info;
+ put_user_try {
+ put_user_ex(regs->pt.bx, &user->regs.ebx);
+ put_user_ex(regs->pt.cx, &user->regs.ecx);
+ put_user_ex(regs->pt.dx, &user->regs.edx);
+ put_user_ex(regs->pt.si, &user->regs.esi);
+ put_user_ex(regs->pt.di, &user->regs.edi);
+ put_user_ex(regs->pt.bp, &user->regs.ebp);
+ put_user_ex(regs->pt.ax, &user->regs.eax);
+ put_user_ex(regs->pt.ip, &user->regs.eip);
+ put_user_ex(regs->pt.cs, &user->regs.cs);
+ put_user_ex(regs->pt.flags, &user->regs.eflags);
+ put_user_ex(regs->pt.sp, &user->regs.esp);
+ put_user_ex(regs->pt.ss, &user->regs.ss);
+ put_user_ex(regs->es, &user->regs.es);
+ put_user_ex(regs->ds, &user->regs.ds);
+ put_user_ex(regs->fs, &user->regs.fs);
+ put_user_ex(regs->gs, &user->regs.gs);
+
+ put_user_ex(tsk->thread.screen_bitmap, &user->screen_bitmap);
+ } put_user_catch(err);
+ if (err) {
pr_alert("could not access userspace vm86_info\n");
do_exit(SIGSEGV);
}
tss = &per_cpu(cpu_tss, get_cpu());
- current->thread.sp0 = current->thread.saved_sp0;
- current->thread.sysenter_cs = __KERNEL_CS;
- load_sp0(tss, ¤t->thread);
- current->thread.saved_sp0 = 0;
+ tsk->thread.sp0 = tsk->thread.saved_sp0;
+ tsk->thread.sysenter_cs = __KERNEL_CS;
+ load_sp0(tss, &tsk->thread);
+ tsk->thread.saved_sp0 = 0;
put_cpu();
ret = KVM86->regs32;
@@ -199,7 +185,8 @@ out:
static int do_vm86_irq_handling(int subfunction, int irqnumber);
-static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk);
+static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
+ struct kernel_vm86_struct *info);
SYSCALL_DEFINE1(vm86old, struct vm86_struct __user *, v86)
{
@@ -208,21 +195,8 @@ SYSCALL_DEFINE1(vm86old, struct vm86_struct __user *, v86)
* This remains on the stack until we
* return to 32 bit user space.
*/
- struct task_struct *tsk = current;
- int tmp;
- if (tsk->thread.saved_sp0)
- return -EPERM;
- tmp = copy_vm86_regs_from_user(&info.regs, &v86->regs,
- offsetof(struct kernel_vm86_struct, vm86plus) -
- sizeof(info.regs));
- if (tmp)
- return -EFAULT;
- memset(&info.vm86plus, 0, (int)&info.regs32 - (int)&info.vm86plus);
- info.regs32 = current_pt_regs();
- tsk->thread.vm86_info = v86;
- do_sys_vm86(&info, tsk);
- return 0; /* we never return here */
+ return do_sys_vm86((struct vm86plus_struct __user *) v86, false, &info);
}
@@ -233,11 +207,7 @@ SYSCALL_DEFINE2(vm86, unsigned long, cmd, unsigned long, arg)
* This remains on the stack until we
* return to 32 bit user space.
*/
- struct task_struct *tsk;
- int tmp;
- struct vm86plus_struct __user *v86;
- tsk = current;
switch (cmd) {
case VM86_REQUEST_IRQ:
case VM86_FREE_IRQ:
@@ -255,34 +225,64 @@ SYSCALL_DEFINE2(vm86, unsigned long, cmd, unsigned long, arg)
}
/* we come here only for functions VM86_ENTER, VM86_ENTER_NO_BYPASS */
- if (tsk->thread.saved_sp0)
- return -EPERM;
- v86 = (struct vm86plus_struct __user *)arg;
- tmp = copy_vm86_regs_from_user(&info.regs, &v86->regs,
- offsetof(struct kernel_vm86_struct, regs32) -
- sizeof(info.regs));
- if (tmp)
- return -EFAULT;
- info.regs32 = current_pt_regs();
- info.vm86plus.is_vm86pus = 1;
- tsk->thread.vm86_info = (struct vm86_struct __user *)v86;
- do_sys_vm86(&info, tsk);
- return 0; /* we never return here */
+ return do_sys_vm86((struct vm86plus_struct __user *) arg, true, &info);
}
-static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk)
+static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
+ struct kernel_vm86_struct *info)
{
struct tss_struct *tss;
-/*
- * make sure the vm86() system call doesn't try to do anything silly
- */
- info->regs.pt.ds = 0;
- info->regs.pt.es = 0;
- info->regs.pt.fs = 0;
-#ifndef CONFIG_X86_32_LAZY_GS
- info->regs.pt.gs = 0;
-#endif
+ struct task_struct *tsk = current;
+ unsigned long err = 0;
+
+ if (tsk->thread.saved_sp0)
+ return -EPERM;
+
+ memset(info, 0, sizeof(*info));
+ get_user_try {
+ unsigned short seg;
+ get_user_ex(info->regs.pt.bx, &v86->regs.ebx);
+ get_user_ex(info->regs.pt.cx, &v86->regs.ecx);
+ get_user_ex(info->regs.pt.dx, &v86->regs.edx);
+ get_user_ex(info->regs.pt.si, &v86->regs.esi);
+ get_user_ex(info->regs.pt.di, &v86->regs.edi);
+ get_user_ex(info->regs.pt.bp, &v86->regs.ebp);
+ get_user_ex(info->regs.pt.ax, &v86->regs.eax);
+ get_user_ex(info->regs.pt.ip, &v86->regs.eip);
+ get_user_ex(seg, &v86->regs.cs);
+ info->regs.pt.cs = seg;
+ get_user_ex(info->regs.pt.flags, &v86->regs.eflags);
+ get_user_ex(info->regs.pt.sp, &v86->regs.esp);
+ get_user_ex(seg, &v86->regs.ss);
+ info->regs.pt.ss = seg;
+ get_user_ex(info->regs.es, &v86->regs.es);
+ get_user_ex(info->regs.ds, &v86->regs.ds);
+ get_user_ex(info->regs.fs, &v86->regs.fs);
+ get_user_ex(info->regs.gs, &v86->regs.gs);
+
+ get_user_ex(info->flags, &v86->flags);
+ get_user_ex(info->screen_bitmap, &v86->screen_bitmap);
+ get_user_ex(info->cpu_type, &v86->cpu_type);
+ } get_user_catch(err);
+ if (err)
+ return err;
+
+ if (copy_from_user(&info->int_revectored, &v86->int_revectored,
+ sizeof(struct revectored_struct)))
+ return -EFAULT;
+ if (copy_from_user(&info->int21_revectored, &v86->int21_revectored,
+ sizeof(struct revectored_struct)))
+ return -EFAULT;
+ if (plus) {
+ if (copy_from_user(&info->vm86plus, &v86->vm86plus,
+ sizeof(struct vm86plus_info_struct)))
+ return -EFAULT;
+ info->vm86plus.is_vm86pus = 1;
+ }
+
+ info->regs32 = current_pt_regs();
+ tsk->thread.vm86_info = v86;
/*
* The flags register is also special: we cannot trust that the user
@@ -344,7 +344,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
"jmp resume_userspace"
: /* no outputs */
:"r" (&info->regs), "r" (task_thread_info(tsk)), "r" (0));
- /* we never return here */
+ unreachable(); /* we never return here */
}
static inline void return_to_32bit(struct kernel_vm86_regs *regs16, int retval)
--
2.4.3
Allocate a separate structure for the vm86 fields.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/include/asm/processor.h | 12 ++--------
arch/x86/include/asm/vm86.h | 11 +++++++++
arch/x86/kernel/process.c | 7 ++++++
arch/x86/kernel/vm86_32.c | 51 +++++++++++++++++++++++-----------------
arch/x86/mm/fault.c | 4 ++--
5 files changed, 52 insertions(+), 33 deletions(-)
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 98190a6..d636fb8 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -403,16 +403,9 @@ struct thread_struct {
unsigned long cr2;
unsigned long trap_nr;
unsigned long error_code;
-#ifdef CONFIG_X86_32
+#ifdef CONFIG_VM86
/* Virtual 86 mode info */
- struct vm86plus_struct __user *vm86_info;
- unsigned long screen_bitmap;
- unsigned long v86flags;
- unsigned long v86mask;
- unsigned long saved_sp0;
-#ifdef CONFIG_X86_32_LAZY_GS
- unsigned int saved_gs;
-#endif
+ struct kernel_vm86_info *vm86;
#endif
/* IO permissions: */
unsigned long *io_bitmap_ptr;
@@ -717,7 +710,6 @@ static inline void spin_lock_prefetch(const void *x)
#define INIT_THREAD { \
.sp0 = TOP_OF_INIT_STACK, \
- .vm86_info = NULL, \
.sysenter_cs = __KERNEL_CS, \
.io_bitmap_ptr = NULL, \
}
diff --git a/arch/x86/include/asm/vm86.h b/arch/x86/include/asm/vm86.h
index 1d8de3f..07de263 100644
--- a/arch/x86/include/asm/vm86.h
+++ b/arch/x86/include/asm/vm86.h
@@ -58,6 +58,17 @@ struct kernel_vm86_struct {
*/
};
+struct kernel_vm86_info {
+ struct vm86plus_struct __user *vm86_info;
+ unsigned long screen_bitmap;
+ unsigned long v86flags;
+ unsigned long v86mask;
+ unsigned long saved_sp0;
+#ifdef CONFIG_X86_32_LAZY_GS
+ unsigned int saved_gs;
+#endif
+};
+
#ifdef CONFIG_VM86
void handle_vm86_fault(struct kernel_vm86_regs *, long);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 9cad694..5dcd037 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -110,6 +110,13 @@ void exit_thread(void)
kfree(bp);
}
+#ifdef CONFIG_VM86
+ if (t->vm86) {
+ kfree(t->vm86);
+ t->vm86 = NULL;
+ }
+#endif
+
fpu__drop(fpu);
}
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 08bbb69..07dcab1d 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -44,6 +44,7 @@
#include <linux/ptrace.h>
#include <linux/audit.h>
#include <linux/stddef.h>
+#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -81,8 +82,8 @@
/*
* virtual flags (16 and 32-bit versions)
*/
-#define VFLAGS (*(unsigned short *)&(current->thread.v86flags))
-#define VEFLAGS (current->thread.v86flags)
+#define VFLAGS (*(unsigned short *)&(current->thread.vm86->v86flags))
+#define VEFLAGS (current->thread.vm86->v86flags)
#define set_flags(X, new, mask) \
((X) = ((X) & ~(mask)) | ((new) & (mask)))
@@ -96,6 +97,7 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
struct pt_regs *ret;
struct task_struct *tsk = current;
struct vm86plus_struct __user *user;
+ struct kernel_vm86_info *vm86 = current->thread.vm86;
long err = 0;
/*
@@ -105,12 +107,12 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
*/
local_irq_enable();
- if (!tsk->thread.vm86_info) {
+ if (!vm86 || !vm86->vm86_info) {
pr_alert("no vm86_info: BAD\n");
do_exit(SIGSEGV);
}
- set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | tsk->thread.v86mask);
- user = tsk->thread.vm86_info;
+ set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | vm86->v86mask);
+ user = vm86->vm86_info;
put_user_try {
put_user_ex(regs->pt.bx, &user->regs.ebx);
put_user_ex(regs->pt.cx, &user->regs.ecx);
@@ -129,7 +131,7 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
put_user_ex(regs->fs, &user->regs.fs);
put_user_ex(regs->gs, &user->regs.gs);
- put_user_ex(tsk->thread.screen_bitmap, &user->screen_bitmap);
+ put_user_ex(vm86->screen_bitmap, &user->screen_bitmap);
} put_user_catch(err);
if (err) {
pr_alert("could not access userspace vm86_info\n");
@@ -137,15 +139,15 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
}
tss = &per_cpu(cpu_tss, get_cpu());
- tsk->thread.sp0 = tsk->thread.saved_sp0;
+ tsk->thread.sp0 = vm86->saved_sp0;
tsk->thread.sysenter_cs = __KERNEL_CS;
load_sp0(tss, &tsk->thread);
- tsk->thread.saved_sp0 = 0;
+ vm86->saved_sp0 = 0;
put_cpu();
ret = KVM86->regs32;
- lazy_load_gs(current->thread.saved_gs);
+ lazy_load_gs(vm86->saved_gs);
return ret;
}
@@ -234,9 +236,16 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
{
struct tss_struct *tss;
struct task_struct *tsk = current;
+ struct kernel_vm86_info *vm86 = tsk->thread.vm86;
unsigned long err = 0;
- if (tsk->thread.saved_sp0)
+ if (!vm86)
+ {
+ if (!(vm86 = kzalloc(sizeof(*vm86), GFP_KERNEL)))
+ return -ENOMEM;
+ tsk->thread.vm86 = vm86;
+ }
+ if (vm86->saved_sp0)
return -EPERM;
memset(info, 0, sizeof(*info));
@@ -282,7 +291,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
}
info->regs32 = current_pt_regs();
- tsk->thread.vm86_info = v86;
+ vm86->vm86_info = v86;
/*
* The flags register is also special: we cannot trust that the user
@@ -298,16 +307,16 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
switch (info->cpu_type) {
case CPU_286:
- tsk->thread.v86mask = 0;
+ vm86->v86mask = 0;
break;
case CPU_386:
- tsk->thread.v86mask = X86_EFLAGS_NT | X86_EFLAGS_IOPL;
+ vm86->v86mask = X86_EFLAGS_NT | X86_EFLAGS_IOPL;
break;
case CPU_486:
- tsk->thread.v86mask = X86_EFLAGS_AC | X86_EFLAGS_NT | X86_EFLAGS_IOPL;
+ vm86->v86mask = X86_EFLAGS_AC | X86_EFLAGS_NT | X86_EFLAGS_IOPL;
break;
default:
- tsk->thread.v86mask = X86_EFLAGS_ID | X86_EFLAGS_AC | X86_EFLAGS_NT | X86_EFLAGS_IOPL;
+ vm86->v86mask = X86_EFLAGS_ID | X86_EFLAGS_AC | X86_EFLAGS_NT | X86_EFLAGS_IOPL;
break;
}
@@ -315,8 +324,8 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
* Save old state, set default return value (%ax) to 0 (VM86_SIGNAL)
*/
info->regs32->ax = VM86_SIGNAL;
- tsk->thread.saved_sp0 = tsk->thread.sp0;
- lazy_save_gs(tsk->thread.saved_gs);
+ vm86->saved_sp0 = tsk->thread.sp0;
+ lazy_save_gs(vm86->saved_gs);
tss = &per_cpu(cpu_tss, get_cpu());
tsk->thread.sp0 = (unsigned long) &info->VM86_TSS_ESP0;
@@ -325,7 +334,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
load_sp0(tss, &tsk->thread);
put_cpu();
- tsk->thread.screen_bitmap = info->screen_bitmap;
+ vm86->screen_bitmap = info->screen_bitmap;
if (info->flags & VM86_SCREEN_BITMAP)
mark_screen_rdonly(tsk->mm);
@@ -395,7 +404,7 @@ static inline void clear_AC(struct kernel_vm86_regs *regs)
static inline void set_vflags_long(unsigned long flags, struct kernel_vm86_regs *regs)
{
- set_flags(VEFLAGS, flags, current->thread.v86mask);
+ set_flags(VEFLAGS, flags, current->thread.vm86->v86mask);
set_flags(regs->pt.flags, flags, SAFE_MASK);
if (flags & X86_EFLAGS_IF)
set_IF(regs);
@@ -405,7 +414,7 @@ static inline void set_vflags_long(unsigned long flags, struct kernel_vm86_regs
static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs *regs)
{
- set_flags(VFLAGS, flags, current->thread.v86mask);
+ set_flags(VFLAGS, flags, current->thread.vm86->v86mask);
set_flags(regs->pt.flags, flags, SAFE_MASK);
if (flags & X86_EFLAGS_IF)
set_IF(regs);
@@ -420,7 +429,7 @@ static inline unsigned long get_vflags(struct kernel_vm86_regs *regs)
if (VEFLAGS & X86_EFLAGS_VIF)
flags |= X86_EFLAGS_IF;
flags |= X86_EFLAGS_IOPL;
- return flags | (VEFLAGS & current->thread.v86mask);
+ return flags | (VEFLAGS & current->thread.vm86->v86mask);
}
static inline int is_revectored(int nr, struct revectored_struct *bitmap)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 81dcebf..5196ac4 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -315,12 +315,12 @@ check_v8086_mode(struct pt_regs *regs, unsigned long address,
{
unsigned long bit;
- if (!v8086_mode(regs))
+ if (!v8086_mode(regs) || !tsk->thread.vm86)
return;
bit = (address - 0xA0000) >> PAGE_SHIFT;
if (bit < 32)
- tsk->thread.screen_bitmap |= 1 << bit;
+ tsk->thread.vm86->screen_bitmap |= 1 << bit;
}
static bool low_pfn(unsigned long pfn)
--
2.4.3
Move the non-regs fields to the off-stack data.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/include/asm/vm86.h | 16 ++++++++--------
arch/x86/kernel/vm86_32.c | 40 +++++++++++++++++++++-------------------
2 files changed, 29 insertions(+), 27 deletions(-)
diff --git a/arch/x86/include/asm/vm86.h b/arch/x86/include/asm/vm86.h
index 07de263..8e83e57 100644
--- a/arch/x86/include/asm/vm86.h
+++ b/arch/x86/include/asm/vm86.h
@@ -38,13 +38,7 @@ struct kernel_vm86_struct {
* Therefore, pt_regs in fact points to a complete 'kernel_vm86_struct'
* in kernelspace, hence we need not reget the data from userspace.
*/
-#define VM86_TSS_ESP0 flags
- unsigned long flags;
- unsigned long screen_bitmap;
- unsigned long cpu_type;
- struct revectored_struct int_revectored;
- struct revectored_struct int21_revectored;
- struct vm86plus_info_struct vm86plus;
+#define VM86_TSS_ESP0 regs32
struct pt_regs *regs32; /* here we save the pointer to the old regs */
/*
* The below is not part of the structure, but the stack layout continues
@@ -60,13 +54,19 @@ struct kernel_vm86_struct {
struct kernel_vm86_info {
struct vm86plus_struct __user *vm86_info;
- unsigned long screen_bitmap;
unsigned long v86flags;
unsigned long v86mask;
unsigned long saved_sp0;
#ifdef CONFIG_X86_32_LAZY_GS
unsigned int saved_gs;
#endif
+
+ unsigned long flags;
+ unsigned long screen_bitmap;
+ unsigned long cpu_type;
+ struct revectored_struct int_revectored;
+ struct revectored_struct int21_revectored;
+ struct vm86plus_info_struct vm86plus;
};
#ifdef CONFIG_VM86
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 07dcab1d..29a9d92 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -68,7 +68,6 @@
#define KVM86 ((struct kernel_vm86_struct *)regs)
-#define VMPI KVM86->vm86plus
/*
@@ -270,25 +269,27 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
get_user_ex(info->regs.fs, &v86->regs.fs);
get_user_ex(info->regs.gs, &v86->regs.gs);
- get_user_ex(info->flags, &v86->flags);
- get_user_ex(info->screen_bitmap, &v86->screen_bitmap);
- get_user_ex(info->cpu_type, &v86->cpu_type);
+ get_user_ex(vm86->flags, &v86->flags);
+ get_user_ex(vm86->screen_bitmap, &v86->screen_bitmap);
+ get_user_ex(vm86->cpu_type, &v86->cpu_type);
} get_user_catch(err);
if (err)
return err;
- if (copy_from_user(&info->int_revectored, &v86->int_revectored,
+ if (copy_from_user(&vm86->int_revectored, &v86->int_revectored,
sizeof(struct revectored_struct)))
return -EFAULT;
- if (copy_from_user(&info->int21_revectored, &v86->int21_revectored,
+ if (copy_from_user(&vm86->int21_revectored, &v86->int21_revectored,
sizeof(struct revectored_struct)))
return -EFAULT;
if (plus) {
- if (copy_from_user(&info->vm86plus, &v86->vm86plus,
+ if (copy_from_user(&vm86->vm86plus, &v86->vm86plus,
sizeof(struct vm86plus_info_struct)))
return -EFAULT;
- info->vm86plus.is_vm86pus = 1;
- }
+ vm86->vm86plus.is_vm86pus = 1;
+ } else
+ memset(&vm86->vm86plus, 0,
+ sizeof(struct vm86plus_info_struct));
info->regs32 = current_pt_regs();
vm86->vm86_info = v86;
@@ -305,7 +306,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
info->regs.pt.orig_ax = info->regs32->orig_ax;
- switch (info->cpu_type) {
+ switch (vm86->cpu_type) {
case CPU_286:
vm86->v86mask = 0;
break;
@@ -334,8 +335,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
load_sp0(tss, &tsk->thread);
put_cpu();
- vm86->screen_bitmap = info->screen_bitmap;
- if (info->flags & VM86_SCREEN_BITMAP)
+ if (vm86->flags & VM86_SCREEN_BITMAP)
mark_screen_rdonly(tsk->mm);
/*call __audit_syscall_exit since we do not exit via the normal paths */
@@ -527,12 +527,13 @@ static void do_int(struct kernel_vm86_regs *regs, int i,
{
unsigned long __user *intr_ptr;
unsigned long segoffs;
+ struct kernel_vm86_info *vm86 = current->thread.vm86;
if (regs->pt.cs == BIOSSEG)
goto cannot_handle;
- if (is_revectored(i, &KVM86->int_revectored))
+ if (is_revectored(i, &vm86->int_revectored))
goto cannot_handle;
- if (i == 0x21 && is_revectored(AH(regs), &KVM86->int21_revectored))
+ if (i == 0x21 && is_revectored(AH(regs), &vm86->int21_revectored))
goto cannot_handle;
intr_ptr = (unsigned long __user *) (i << 2);
if (get_user(segoffs, intr_ptr))
@@ -556,7 +557,7 @@ cannot_handle:
int handle_vm86_trap(struct kernel_vm86_regs *regs, long error_code, int trapno)
{
- if (VMPI.is_vm86pus) {
+ if (current->thread.vm86->vm86plus.is_vm86pus) {
if ((trapno == 3) || (trapno == 1)) {
KVM86->regs32->ax = VM86_TRAP + (trapno << 8);
/* setting this flag forces the code in entry_32.S to
@@ -583,12 +584,13 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
unsigned char __user *ssp;
unsigned short ip, sp, orig_flags;
int data32, pref_done;
+ struct vm86plus_info_struct *vmpi = ¤t->thread.vm86->vm86plus;
#define CHECK_IF_IN_TRAP \
- if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \
+ if (vmpi->vm86dbg_active && vmpi->vm86dbg_TFpendig) \
newflags |= X86_EFLAGS_TF
#define VM86_FAULT_RETURN do { \
- if (VMPI.force_return_for_pic && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) \
+ if (vmpi->force_return_for_pic && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) \
return_to_32bit(regs, VM86_PICRETURN); \
if (orig_flags & X86_EFLAGS_TF) \
handle_vm86_trap(regs, 0, 1); \
@@ -658,8 +660,8 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
case 0xcd: {
int intno = popb(csp, ip, simulate_sigsegv);
IP(regs) = ip;
- if (VMPI.vm86dbg_active) {
- if ((1 << (intno & 7)) & VMPI.vm86dbg_intxxtab[intno >> 3])
+ if (vmpi->vm86dbg_active) {
+ if ((1 << (intno & 7)) & vmpi->vm86dbg_intxxtab[intno >> 3])
return_to_32bit(regs, VM86_INTx + (intno << 8));
}
do_int(regs, intno, ssp, sp);
--
2.4.3
Now there is no vm86-specific data left on the kernel stack while in
userspace, except for the 32-bit regs.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/include/asm/vm86.h | 25 +------------
arch/x86/kernel/vm86_32.c | 91 +++++++++++++++++++--------------------------
2 files changed, 40 insertions(+), 76 deletions(-)
diff --git a/arch/x86/include/asm/vm86.h b/arch/x86/include/asm/vm86.h
index 8e83e57..6573145 100644
--- a/arch/x86/include/asm/vm86.h
+++ b/arch/x86/include/asm/vm86.h
@@ -28,32 +28,9 @@ struct kernel_vm86_regs {
unsigned short gs, __gsh;
};
-struct kernel_vm86_struct {
- struct kernel_vm86_regs regs;
-/*
- * the below part remains on the kernel stack while we are in VM86 mode.
- * 'tss.esp0' then contains the address of VM86_TSS_ESP0 below, and when we
- * get forced back from VM86, the CPU and "SAVE_ALL" will restore the above
- * 'struct kernel_vm86_regs' with the then actual values.
- * Therefore, pt_regs in fact points to a complete 'kernel_vm86_struct'
- * in kernelspace, hence we need not reget the data from userspace.
- */
-#define VM86_TSS_ESP0 regs32
- struct pt_regs *regs32; /* here we save the pointer to the old regs */
-/*
- * The below is not part of the structure, but the stack layout continues
- * this way. In front of 'return-eip' may be some data, depending on
- * compilation, so we don't rely on this and save the pointer to 'oldregs'
- * in 'regs32' above.
- * However, with GCC-2.7.2 and the current CFLAGS you see exactly this:
-
- long return-eip; from call to vm86()
- struct pt_regs oldregs; user space registers as saved by syscall
- */
-};
-
struct kernel_vm86_info {
struct vm86plus_struct __user *vm86_info;
+ struct pt_regs *regs32;
unsigned long v86flags;
unsigned long v86mask;
unsigned long saved_sp0;
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 29a9d92..0f807be 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -67,9 +67,6 @@
*/
-#define KVM86 ((struct kernel_vm86_struct *)regs)
-
-
/*
* 8- and 16-bit register defines..
*/
@@ -144,7 +141,7 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
vm86->saved_sp0 = 0;
put_cpu();
- ret = KVM86->regs32;
+ ret = vm86->regs32;
lazy_load_gs(vm86->saved_gs);
@@ -186,29 +183,16 @@ out:
static int do_vm86_irq_handling(int subfunction, int irqnumber);
-static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
- struct kernel_vm86_struct *info);
+static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus);
SYSCALL_DEFINE1(vm86old, struct vm86_struct __user *, v86)
{
- struct kernel_vm86_struct info; /* declare this _on top_,
- * this avoids wasting of stack space.
- * This remains on the stack until we
- * return to 32 bit user space.
- */
-
- return do_sys_vm86((struct vm86plus_struct __user *) v86, false, &info);
+ return do_sys_vm86((struct vm86plus_struct __user *) v86, false);
}
SYSCALL_DEFINE2(vm86, unsigned long, cmd, unsigned long, arg)
{
- struct kernel_vm86_struct info; /* declare this _on top_,
- * this avoids wasting of stack space.
- * This remains on the stack until we
- * return to 32 bit user space.
- */
-
switch (cmd) {
case VM86_REQUEST_IRQ:
case VM86_FREE_IRQ:
@@ -226,16 +210,17 @@ SYSCALL_DEFINE2(vm86, unsigned long, cmd, unsigned long, arg)
}
/* we come here only for functions VM86_ENTER, VM86_ENTER_NO_BYPASS */
- return do_sys_vm86((struct vm86plus_struct __user *) arg, true, &info);
+ return do_sys_vm86((struct vm86plus_struct __user *) arg, true);
}
-static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
- struct kernel_vm86_struct *info)
+static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus)
{
struct tss_struct *tss;
struct task_struct *tsk = current;
struct kernel_vm86_info *vm86 = tsk->thread.vm86;
+ struct kernel_vm86_regs vm86regs;
+ struct pt_regs *regs32 = current_pt_regs();
unsigned long err = 0;
if (!vm86)
@@ -247,27 +232,27 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
if (vm86->saved_sp0)
return -EPERM;
- memset(info, 0, sizeof(*info));
+ memset(&vm86regs, 0, sizeof(vm86regs));
get_user_try {
unsigned short seg;
- get_user_ex(info->regs.pt.bx, &v86->regs.ebx);
- get_user_ex(info->regs.pt.cx, &v86->regs.ecx);
- get_user_ex(info->regs.pt.dx, &v86->regs.edx);
- get_user_ex(info->regs.pt.si, &v86->regs.esi);
- get_user_ex(info->regs.pt.di, &v86->regs.edi);
- get_user_ex(info->regs.pt.bp, &v86->regs.ebp);
- get_user_ex(info->regs.pt.ax, &v86->regs.eax);
- get_user_ex(info->regs.pt.ip, &v86->regs.eip);
+ get_user_ex(vm86regs.pt.bx, &v86->regs.ebx);
+ get_user_ex(vm86regs.pt.cx, &v86->regs.ecx);
+ get_user_ex(vm86regs.pt.dx, &v86->regs.edx);
+ get_user_ex(vm86regs.pt.si, &v86->regs.esi);
+ get_user_ex(vm86regs.pt.di, &v86->regs.edi);
+ get_user_ex(vm86regs.pt.bp, &v86->regs.ebp);
+ get_user_ex(vm86regs.pt.ax, &v86->regs.eax);
+ get_user_ex(vm86regs.pt.ip, &v86->regs.eip);
get_user_ex(seg, &v86->regs.cs);
- info->regs.pt.cs = seg;
- get_user_ex(info->regs.pt.flags, &v86->regs.eflags);
- get_user_ex(info->regs.pt.sp, &v86->regs.esp);
+ vm86regs.pt.cs = seg;
+ get_user_ex(vm86regs.pt.flags, &v86->regs.eflags);
+ get_user_ex(vm86regs.pt.sp, &v86->regs.esp);
get_user_ex(seg, &v86->regs.ss);
- info->regs.pt.ss = seg;
- get_user_ex(info->regs.es, &v86->regs.es);
- get_user_ex(info->regs.ds, &v86->regs.ds);
- get_user_ex(info->regs.fs, &v86->regs.fs);
- get_user_ex(info->regs.gs, &v86->regs.gs);
+ vm86regs.pt.ss = seg;
+ get_user_ex(vm86regs.es, &v86->regs.es);
+ get_user_ex(vm86regs.ds, &v86->regs.ds);
+ get_user_ex(vm86regs.fs, &v86->regs.fs);
+ get_user_ex(vm86regs.gs, &v86->regs.gs);
get_user_ex(vm86->flags, &v86->flags);
get_user_ex(vm86->screen_bitmap, &v86->screen_bitmap);
@@ -290,8 +275,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
} else
memset(&vm86->vm86plus, 0,
sizeof(struct vm86plus_info_struct));
-
- info->regs32 = current_pt_regs();
+ vm86->regs32 = regs32;
vm86->vm86_info = v86;
/*
@@ -299,12 +283,12 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
* has set it up safely, so this makes sure interrupt etc flags are
* inherited from protected mode.
*/
- VEFLAGS = info->regs.pt.flags;
- info->regs.pt.flags &= SAFE_MASK;
- info->regs.pt.flags |= info->regs32->flags & ~SAFE_MASK;
- info->regs.pt.flags |= X86_VM_MASK;
+ VEFLAGS = vm86regs.pt.flags;
+ vm86regs.pt.flags &= SAFE_MASK;
+ vm86regs.pt.flags |= regs32->flags & ~SAFE_MASK;
+ vm86regs.pt.flags |= X86_VM_MASK;
- info->regs.pt.orig_ax = info->regs32->orig_ax;
+ vm86regs.pt.orig_ax = regs32->orig_ax;
switch (vm86->cpu_type) {
case CPU_286:
@@ -324,12 +308,13 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
/*
* Save old state, set default return value (%ax) to 0 (VM86_SIGNAL)
*/
- info->regs32->ax = VM86_SIGNAL;
+ regs32->ax = VM86_SIGNAL;
vm86->saved_sp0 = tsk->thread.sp0;
lazy_save_gs(vm86->saved_gs);
tss = &per_cpu(cpu_tss, get_cpu());
- tsk->thread.sp0 = (unsigned long) &info->VM86_TSS_ESP0;
+ /* Set new sp0 right below 32-bit regs */
+ tsk->thread.sp0 = (unsigned long) regs32;
if (cpu_has_sep)
tsk->thread.sysenter_cs = 0;
load_sp0(tss, &tsk->thread);
@@ -352,7 +337,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
#endif
"jmp resume_userspace"
: /* no outputs */
- :"r" (&info->regs), "r" (task_thread_info(tsk)), "r" (0));
+ :"r" (&vm86regs), "r" (task_thread_info(tsk)), "r" (0));
unreachable(); /* we never return here */
}
@@ -557,12 +542,14 @@ cannot_handle:
int handle_vm86_trap(struct kernel_vm86_regs *regs, long error_code, int trapno)
{
- if (current->thread.vm86->vm86plus.is_vm86pus) {
+ struct kernel_vm86_info *vm86 = current->thread.vm86;
+
+ if (vm86->vm86plus.is_vm86pus) {
if ((trapno == 3) || (trapno == 1)) {
- KVM86->regs32->ax = VM86_TRAP + (trapno << 8);
+ vm86->regs32->ax = VM86_TRAP + (trapno << 8);
/* setting this flag forces the code in entry_32.S to
the path where we call save_v86_state() and change
- the stack pointer to KVM86->regs32 */
+ the stack pointer to regs32 */
set_thread_flag(TIF_NOTIFY_RESUME);
return 0;
}
--
2.4.3
Change to use the normal pt_regs area to enter and exit vm86 mode. This is
done by increasing the padding at the top of the stack to make room for the
extra vm86 segment slots in the IRET frame. It then saves the 32-bit regs
in the off-stack vm86 data, and copies in the vm86 regs. Exiting back to
32-bit mode does the reverse. This allows removing the hacks to jump directly
into the exit asm code due to having to change the stack pointer. Returning
normally from the vm86 syscall and the exception handlers allows things like
ptrace and auditing to work properly.
Signed-off-by: Brian Gerst <[email protected]>
---
arch/x86/entry/entry_32.S | 24 +--------
arch/x86/include/asm/thread_info.h | 11 ++--
arch/x86/include/asm/vm86.h | 6 ++-
arch/x86/kernel/signal.c | 3 ++
arch/x86/kernel/vm86_32.c | 106 +++++++++++++++----------------------
5 files changed, 58 insertions(+), 92 deletions(-)
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index 21dc60a..f940e24 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -525,34 +525,12 @@ work_resched:
work_notifysig: # deal with pending signals and
# notify-resume requests
-#ifdef CONFIG_VM86
- testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
- movl %esp, %eax
- jnz work_notifysig_v86 # returning to kernel-space or
- # vm86-space
-1:
-#else
- movl %esp, %eax
-#endif
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
- movb PT_CS(%esp), %bl
- andb $SEGMENT_RPL_MASK, %bl
- cmpb $USER_RPL, %bl
- jb resume_kernel
+ movl %esp, %eax
xorl %edx, %edx
call do_notify_resume
jmp resume_userspace
-
-#ifdef CONFIG_VM86
- ALIGN
-work_notifysig_v86:
- pushl %ecx # save ti_flags for do_notify_resume
- call save_v86_state # %eax contains pt_regs pointer
- popl %ecx
- movl %eax, %esp
- jmp 1b
-#endif
END(work_pending)
# perform syscall exit tracing
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index 225ee54..fdad5c2 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -27,14 +27,17 @@
* Without this offset, that can result in a page fault. (We are
* careful that, in this case, the value we read doesn't matter.)
*
- * In vm86 mode, the hardware frame is much longer still, but we neither
- * access the extra members from NMI context, nor do we write such a
- * frame at sp0 at all.
+ * In vm86 mode, the hardware frame is much longer still, so add 16
+ * bytes to make room for the real-mode segments.
*
* x86_64 has a fixed-length stack frame.
*/
#ifdef CONFIG_X86_32
-# define TOP_OF_KERNEL_STACK_PADDING 8
+# ifdef CONFIG_VM86
+# define TOP_OF_KERNEL_STACK_PADDING 16
+# else
+# define TOP_OF_KERNEL_STACK_PADDING 8
+# endif
#else
# define TOP_OF_KERNEL_STACK_PADDING 0
#endif
diff --git a/arch/x86/include/asm/vm86.h b/arch/x86/include/asm/vm86.h
index 6573145..736c789a 100644
--- a/arch/x86/include/asm/vm86.h
+++ b/arch/x86/include/asm/vm86.h
@@ -30,7 +30,7 @@ struct kernel_vm86_regs {
struct kernel_vm86_info {
struct vm86plus_struct __user *vm86_info;
- struct pt_regs *regs32;
+ struct pt_regs regs32;
unsigned long v86flags;
unsigned long v86mask;
unsigned long saved_sp0;
@@ -50,7 +50,7 @@ struct kernel_vm86_info {
void handle_vm86_fault(struct kernel_vm86_regs *, long);
int handle_vm86_trap(struct kernel_vm86_regs *, long, int);
-struct pt_regs *save_v86_state(struct kernel_vm86_regs *);
+void save_v86_state(struct kernel_vm86_regs *, int);
struct task_struct;
void release_vm86_irqs(struct task_struct *);
@@ -65,6 +65,8 @@ static inline int handle_vm86_trap(struct kernel_vm86_regs *a, long b, int c)
return 0;
}
+static inline void save_v86_state(struct kernel_vm86_regs *, int) { }
+
#endif /* CONFIG_VM86 */
#endif /* _ASM_X86_VM86_H */
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 7e88cc7..bfd736e 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -635,6 +635,9 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
bool stepping, failed;
struct fpu *fpu = ¤t->thread.fpu;
+ if (v8086_mode(regs))
+ save_v86_state((struct kernel_vm86_regs *) regs, VM86_SIGNAL);
+
/* Are we from a system call? */
if (syscall_get_nr(current, regs) >= 0) {
/* If so, check system call restarting.. */
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 0f807be..d74aaa8 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -50,6 +50,7 @@
#include <asm/io.h>
#include <asm/tlbflush.h>
#include <asm/irq.h>
+#include <asm/traps.h>
/*
* Known problems:
@@ -87,10 +88,9 @@
#define SAFE_MASK (0xDD5)
#define RETURN_MASK (0xDFF)
-struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
+void save_v86_state(struct kernel_vm86_regs *regs, int retval)
{
struct tss_struct *tss;
- struct pt_regs *ret;
struct task_struct *tsk = current;
struct vm86plus_struct __user *user;
struct kernel_vm86_info *vm86 = current->thread.vm86;
@@ -141,11 +141,11 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
vm86->saved_sp0 = 0;
put_cpu();
- ret = vm86->regs32;
+ memcpy(®s->pt, &vm86->regs32, sizeof(struct pt_regs));
lazy_load_gs(vm86->saved_gs);
- return ret;
+ regs->pt.ax = retval;
}
static void mark_screen_rdonly(struct mm_struct *mm)
@@ -220,7 +220,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus)
struct task_struct *tsk = current;
struct kernel_vm86_info *vm86 = tsk->thread.vm86;
struct kernel_vm86_regs vm86regs;
- struct pt_regs *regs32 = current_pt_regs();
+ struct pt_regs *regs = current_pt_regs();
unsigned long err = 0;
if (!vm86)
@@ -275,7 +275,8 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus)
} else
memset(&vm86->vm86plus, 0,
sizeof(struct vm86plus_info_struct));
- vm86->regs32 = regs32;
+
+ memcpy(&vm86->regs32, regs, sizeof(struct pt_regs));
vm86->vm86_info = v86;
/*
@@ -285,10 +286,10 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus)
*/
VEFLAGS = vm86regs.pt.flags;
vm86regs.pt.flags &= SAFE_MASK;
- vm86regs.pt.flags |= regs32->flags & ~SAFE_MASK;
+ vm86regs.pt.flags |= regs->flags & ~SAFE_MASK;
vm86regs.pt.flags |= X86_VM_MASK;
- vm86regs.pt.orig_ax = regs32->orig_ax;
+ vm86regs.pt.orig_ax = regs->orig_ax;
switch (vm86->cpu_type) {
case CPU_286:
@@ -306,15 +307,14 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus)
}
/*
- * Save old state, set default return value (%ax) to 0 (VM86_SIGNAL)
+ * Save old state
*/
- regs32->ax = VM86_SIGNAL;
vm86->saved_sp0 = tsk->thread.sp0;
lazy_save_gs(vm86->saved_gs);
tss = &per_cpu(cpu_tss, get_cpu());
- /* Set new sp0 right below 32-bit regs */
- tsk->thread.sp0 = (unsigned long) regs32;
+ /* make room for real-mode segments */
+ tsk->thread.sp0 += 16;
if (cpu_has_sep)
tsk->thread.sysenter_cs = 0;
load_sp0(tss, &tsk->thread);
@@ -323,41 +323,14 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus)
if (vm86->flags & VM86_SCREEN_BITMAP)
mark_screen_rdonly(tsk->mm);
- /*call __audit_syscall_exit since we do not exit via the normal paths */
-#ifdef CONFIG_AUDITSYSCALL
- if (unlikely(current->audit_context))
- __audit_syscall_exit(1, 0);
-#endif
-
- __asm__ __volatile__(
- "movl %0,%%esp\n\t"
- "movl %1,%%ebp\n\t"
-#ifdef CONFIG_X86_32_LAZY_GS
- "mov %2, %%gs\n\t"
-#endif
- "jmp resume_userspace"
- : /* no outputs */
- :"r" (&vm86regs), "r" (task_thread_info(tsk)), "r" (0));
- unreachable(); /* we never return here */
-}
-
-static inline void return_to_32bit(struct kernel_vm86_regs *regs16, int retval)
-{
- struct pt_regs *regs32;
-
- regs32 = save_v86_state(regs16);
- regs32->ax = retval;
- __asm__ __volatile__("movl %0,%%esp\n\t"
- "movl %1,%%ebp\n\t"
- "jmp resume_userspace"
- : : "r" (regs32), "r" (current_thread_info()));
+ memcpy((struct kernel_vm86_regs *)regs, &vm86regs, sizeof(vm86regs));
+ force_iret();
+ return regs->ax;
}
static inline void set_IF(struct kernel_vm86_regs *regs)
{
VEFLAGS |= X86_EFLAGS_VIF;
- if (VEFLAGS & X86_EFLAGS_VIP)
- return_to_32bit(regs, VM86_STI);
}
static inline void clear_IF(struct kernel_vm86_regs *regs)
@@ -537,7 +510,7 @@ static void do_int(struct kernel_vm86_regs *regs, int i,
return;
cannot_handle:
- return_to_32bit(regs, VM86_INTx + (i << 8));
+ save_v86_state(regs, VM86_INTx + (i << 8));
}
int handle_vm86_trap(struct kernel_vm86_regs *regs, long error_code, int trapno)
@@ -546,11 +519,7 @@ int handle_vm86_trap(struct kernel_vm86_regs *regs, long error_code, int trapno)
if (vm86->vm86plus.is_vm86pus) {
if ((trapno == 3) || (trapno == 1)) {
- vm86->regs32->ax = VM86_TRAP + (trapno << 8);
- /* setting this flag forces the code in entry_32.S to
- the path where we call save_v86_state() and change
- the stack pointer to regs32 */
- set_thread_flag(TIF_NOTIFY_RESUME);
+ save_v86_state(regs, VM86_TRAP + (trapno << 8));
return 0;
}
do_int(regs, trapno, (unsigned char __user *) (regs->pt.ss << 4), SP(regs));
@@ -576,12 +545,6 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
#define CHECK_IF_IN_TRAP \
if (vmpi->vm86dbg_active && vmpi->vm86dbg_TFpendig) \
newflags |= X86_EFLAGS_TF
-#define VM86_FAULT_RETURN do { \
- if (vmpi->force_return_for_pic && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) \
- return_to_32bit(regs, VM86_PICRETURN); \
- if (orig_flags & X86_EFLAGS_TF) \
- handle_vm86_trap(regs, 0, 1); \
- return; } while (0)
orig_flags = *(unsigned short *)®s->pt.flags;
@@ -620,7 +583,7 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
SP(regs) -= 2;
}
IP(regs) = ip;
- VM86_FAULT_RETURN;
+ goto vm86_fault_return;
/* popf */
case 0x9d:
@@ -640,7 +603,7 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
else
set_vflags_short(newflags, regs);
- VM86_FAULT_RETURN;
+ goto check_vip;
}
/* int xx */
@@ -648,8 +611,10 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
int intno = popb(csp, ip, simulate_sigsegv);
IP(regs) = ip;
if (vmpi->vm86dbg_active) {
- if ((1 << (intno & 7)) & vmpi->vm86dbg_intxxtab[intno >> 3])
- return_to_32bit(regs, VM86_INTx + (intno << 8));
+ if ((1 << (intno & 7)) & vmpi->vm86dbg_intxxtab[intno >> 3]) {
+ save_v86_state(regs, VM86_INTx + (intno << 8));
+ return;
+ }
}
do_int(regs, intno, ssp, sp);
return;
@@ -680,14 +645,14 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
} else {
set_vflags_short(newflags, regs);
}
- VM86_FAULT_RETURN;
+ goto check_vip;
}
/* cli */
case 0xfa:
IP(regs) = ip;
clear_IF(regs);
- VM86_FAULT_RETURN;
+ goto vm86_fault_return;
/* sti */
/*
@@ -699,12 +664,27 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
case 0xfb:
IP(regs) = ip;
set_IF(regs);
- VM86_FAULT_RETURN;
+ goto check_vip;
default:
- return_to_32bit(regs, VM86_UNKNOWN);
+ save_v86_state(regs, VM86_UNKNOWN);
+ }
+
+ return;
+
+check_vip:
+ if (VEFLAGS & X86_EFLAGS_VIP) {
+ save_v86_state(regs, VM86_STI);
+ return;
}
+vm86_fault_return:
+ if (vmpi->force_return_for_pic && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) {
+ save_v86_state(regs, VM86_PICRETURN);
+ return;
+ }
+ if (orig_flags & X86_EFLAGS_TF)
+ handle_vm86_trap(regs, 0, X86_TRAP_DB);
return;
simulate_sigsegv:
@@ -718,7 +698,7 @@ simulate_sigsegv:
* should be a mixture of the two, but how do we
* get the information? [KD]
*/
- return_to_32bit(regs, VM86_UNKNOWN);
+ save_v86_state(regs, VM86_UNKNOWN);
}
/* ---------------- vm86 special IRQ passing stuff ----------------- */
--
2.4.3
On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
> Move the userspace accesses down into the common function in
> preparation for the next set of patches.
Nice!
Could you improve the changelog a bit? You're really changing the way
it works (in a good way).
> - set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | current->thread.v86mask);
> - tmp = copy_vm86_regs_to_user(¤t->thread.vm86_info->regs, regs);
> - tmp += put_user(current->thread.screen_bitmap, ¤t->thread.vm86_info->screen_bitmap);
> - if (tmp) {
> + set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | tsk->thread.v86mask);
> + user = tsk->thread.vm86_info;
> + put_user_try {
I think that put_user_try/put_user_ex requires access_ok.
> + get_user_try {
> + unsigned short seg;
This also needs access_ok, I think.
--Andy
On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
> There is no need to save FS and non-lazy GS outside the 32-bit regs. Lazy GS
> still needs to be saved because it wasn't saved on syscall entry.
I think I'm missing something. Why can't you just lazy_save_gs into regs32->gs?
--Andy
On Thu, Jul 16, 2015 at 11:45 AM, Andy Lutomirski <[email protected]> wrote:
> On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
>> There is no need to save FS and non-lazy GS outside the 32-bit regs. Lazy GS
>> still needs to be saved because it wasn't saved on syscall entry.
>
> I think I'm missing something. Why can't you just lazy_save_gs into regs32->gs?
>
> --Andy
That is probably a better idea. The gs field exists in pt_regs but is
unused in lazy mode. Could also change switch_to() to store GS in
pt_regs instead of thread.gs, which would simplify ptrace a bit.
--
Brian Gerst
On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
> There is no legitimate reason for usermode to modify the orig_ax field on
> entry to vm86 mode, so copy it from the 32-bit regs.
Looks good.
--Andy
On Thu, Jul 16, 2015 at 11:42 AM, Andy Lutomirski <[email protected]> wrote:
> On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
>> Move the userspace accesses down into the common function in
>> preparation for the next set of patches.
>
> Nice!
>
> Could you improve the changelog a bit? You're really changing the way
> it works (in a good way).
>
>
>> - set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | current->thread.v86mask);
>> - tmp = copy_vm86_regs_to_user(¤t->thread.vm86_info->regs, regs);
>> - tmp += put_user(current->thread.screen_bitmap, ¤t->thread.vm86_info->screen_bitmap);
>> - if (tmp) {
>> + set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | tsk->thread.v86mask);
>> + user = tsk->thread.vm86_info;
>> + put_user_try {
>
> I think that put_user_try/put_user_ex requires access_ok.
>
>
>> + get_user_try {
>> + unsigned short seg;
>
> This also needs access_ok, I think.
Good catch. I modeled this off restore_sigcontext(), but the
access_ok() is actually in the caller.
--
Brian Gerst
On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
> Allocate a separate structure for the vm86 fields.
>
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -315,12 +315,12 @@ check_v8086_mode(struct pt_regs *regs, unsigned long address,
> {
> unsigned long bit;
>
> - if (!v8086_mode(regs))
> + if (!v8086_mode(regs) || !tsk->thread.vm86)
> return;
>
What's this for? Shouldn't that "if (!v8086_mode(regs) ||
WARN_ON(!tsk->thread.vm86))"?
--Andy
On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
> Move the non-regs fields to the off-stack data.
Acked-by: Andy Lutomirski <[email protected]>
>
> Signed-off-by: Brian Gerst <[email protected]>
--Andy
On Thu, Jul 16, 2015 at 4:46 AM, Brian Gerst <[email protected]> wrote:
> Now there is no vm86-specific data left on the kernel stack while in
> userspace, except for the 32-bit regs.
>
Acked-by: Andy Lutomirski <[email protected]>
> Signed-off-by: Brian Gerst <[email protected]>
> ---