The following commit has been merged into the x86/cpu branch of tip:

Commit-ID:     b91e7089ae70d2f7c81a4456e5b78fef498663d9
Gitweb:        
https://git.kernel.org/tip/b91e7089ae70d2f7c81a4456e5b78fef498663d9
Author:        Brendan Shanks <bsha...@codeweavers.com>
AuthorDate:    Fri, 10 Jul 2020 15:45:25 -07:00
Committer:     Borislav Petkov <b...@suse.de>
CommitterDate: Thu, 20 Aug 2020 19:10:26 +02:00

x86/umip: Add emulation/spoofing for SLDT and STR instructions

Add emulation/spoofing of SLDT and STR for both 32- and 64-bit
processes.

Wine users have found a small number of Windows apps using SLDT that
were crashing when run on UMIP-enabled systems.

Originally-by: Ricardo Neri <ricardo.neri-calde...@linux.intel.com>
Reported-by: Andreas Rammhold <andi@notmuch.email>
Signed-off-by: Brendan Shanks <bsha...@codeweavers.com>
Signed-off-by: Borislav Petkov <b...@suse.de>
Acked-by: Andy Lutomirski <l...@kernel.org>
Reviewed-by: Ricardo Neri <ricardo.neri-calde...@linux.intel.com>
Tested-by: Ricardo Neri <ricardo.neri-calde...@linux.intel.com>
Link: https://lkml.kernel.org/r/20200710224525.21966-1-bsha...@codeweavers.com
---
 arch/x86/kernel/umip.c | 40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index 8d5cbe1..2c304fd 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -45,11 +45,12 @@
  * value that, lies close to the top of the kernel memory. The limit for the 
GDT
  * and the IDT are set to zero.
  *
- * Given that SLDT and STR are not commonly used in programs that run on WineHQ
- * or DOSEMU2, they are not emulated.
- *
- * The instruction smsw is emulated to return the value that the register CR0
+ * The instruction SMSW is emulated to return the value that the register CR0
  * has at boot time as set in the head_32.
+ * SLDT and STR are emulated to return the values that the kernel 
programmatically
+ * assigns:
+ * - SLDT returns (GDT_ENTRY_LDT * 8) if an LDT has been set, 0 if not.
+ * - STR returns (GDT_ENTRY_TSS * 8).
  *
  * Emulation is provided for both 32-bit and 64-bit processes.
  *
@@ -244,16 +245,34 @@ static int emulate_umip_insn(struct insn *insn, int 
umip_inst,
                *data_size += UMIP_GDT_IDT_LIMIT_SIZE;
                memcpy(data, &dummy_limit, UMIP_GDT_IDT_LIMIT_SIZE);
 
-       } else if (umip_inst == UMIP_INST_SMSW) {
-               unsigned long dummy_value = CR0_STATE;
+       } else if (umip_inst == UMIP_INST_SMSW || umip_inst == UMIP_INST_SLDT ||
+                  umip_inst == UMIP_INST_STR) {
+               unsigned long dummy_value;
+
+               if (umip_inst == UMIP_INST_SMSW) {
+                       dummy_value = CR0_STATE;
+               } else if (umip_inst == UMIP_INST_STR) {
+                       dummy_value = GDT_ENTRY_TSS * 8;
+               } else if (umip_inst == UMIP_INST_SLDT) {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
+                       down_read(&current->mm->context.ldt_usr_sem);
+                       if (current->mm->context.ldt)
+                               dummy_value = GDT_ENTRY_LDT * 8;
+                       else
+                               dummy_value = 0;
+                       up_read(&current->mm->context.ldt_usr_sem);
+#else
+                       dummy_value = 0;
+#endif
+               }
 
                /*
-                * Even though the CR0 register has 4 bytes, the number
+                * For these 3 instructions, the number
                 * of bytes to be copied in the result buffer is determined
                 * by whether the operand is a register or a memory location.
                 * If operand is a register, return as many bytes as the operand
                 * size. If operand is memory, return only the two least
-                * siginificant bytes of CR0.
+                * siginificant bytes.
                 */
                if (X86_MODRM_MOD(insn->modrm.value) == 3)
                        *data_size = insn->opnd_bytes;
@@ -261,7 +280,6 @@ static int emulate_umip_insn(struct insn *insn, int 
umip_inst,
                        *data_size = 2;
 
                memcpy(data, &dummy_value, *data_size);
-       /* STR and SLDT  are not emulated */
        } else {
                return -EINVAL;
        }
@@ -383,10 +401,6 @@ bool fixup_umip_exception(struct pt_regs *regs)
        umip_pr_warn(regs, "%s instruction cannot be used by applications.\n",
                        umip_insns[umip_inst]);
 
-       /* Do not emulate (spoof) SLDT or STR. */
-       if (umip_inst == UMIP_INST_STR || umip_inst == UMIP_INST_SLDT)
-               return false;
-
        umip_pr_warn(regs, "For now, expensive software emulation returns the 
result.\n");
 
        if (emulate_umip_insn(&insn, umip_inst, dummy_data, &dummy_data_size,

Reply via email to