public inbox for linux-msdos@vger.kernel.org
 help / color / mirror / Atom feed
From: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
To: Andy Lutomirski <luto@amacapital.net>
Cc: Ingo Molnar <mingo@redhat.com>,
	Thomas Gleixner <tglx@linutronix.de>,
	Borislav Petkov <bp@suse.de>, Andy Lutomirski <luto@kernel.org>,
	Peter Zijlstra <peterz@infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	X86 ML <x86@kernel.org>,
	linux-msdos@vger.kernel.org, wine-devel@winehq.org,
	Andrew Morton <akpm@linux-foundation.org>,
	"H . Peter Anvin" <hpa@zytor.com>,
	Brian Gerst <brgerst@gmail.com>, Chen Yucong <slaoub@gmail.com>,
	Chris Metcalf <cmetcalf@mellanox.com>,
	Dave Hansen <dave.hansen@linux.intel.com>,
	Fenghua Yu <fenghua.yu@intel.com>, Huang Rui <ray.huang@amd.com>,
	Jiri Slaby <jslaby@suse.cz>, Jonathan Corbet <corbet@lwn.net>,
	"Michael S . Tsirkin" <mst@redhat.com>,
	Paul Gortmaker <paul.gortmaker@windriver.com>,
	Ravi
Subject: Re: [v2 5/7] x86: Add emulation code for UMIP instructions
Date: Tue, 27 Dec 2016 16:39:42 -0800	[thread overview]
Message-ID: <1482885582.106950.29.camel@ranerica-desktop> (raw)
In-Reply-To: <CALCETrWkL_RumwxcTKaQC=kBKZE4P5u95=OpcPGEjCJfaN9wVA@mail.gmail.com>

On Fri, 2016-12-23 at 18:11 -0800, Andy Lutomirski wrote:
> On Fri, Dec 23, 2016 at 5:37 PM, Ricardo Neri
> <ricardo.neri-calderon@linux.intel.com> wrote:
> > The feature User-Mode Instruction Prevention present in recent Intel
> > processor prevents a group of instructions from being executed with
> > CPL > 0. Otherwise, a general protection fault is issued.
> >
> > Rather than relaying this fault to the user space (in the form of a SIGSEGV
> > signal), the instructions protected by UMIP can be emulated to provide
> > dummy results. This allows to conserve the current kernel behavior and not
> > reveal the system resources that UMIP intends to protect (the global
> > descriptor and interrupt descriptor tables, the segment selectors of the
> > local descriptor table and the task state and the machine status word).
> >
> > This emulation is needed because certain applications (e.g., WineHQ) rely
> > on this subset of instructions to function.
> >
> > The instructions protected by UMIP can be split in two groups. Those who
> > return a kernel memory address (sgdt and sidt) and those who return a
> > value (sldt, str and smsw).
> >
> > For the instructions that return a kernel memory address, the result is
> > emulated as the location of a dummy variable in the kernel memory space.
> > This is needed as applications such as WineHQ rely on the result being
> > located in the kernel memory space function. The limit for the GDT and the
> > IDT are set to zero.
> 
> Nak.  This is a trivial KASLR bypass.  Just give them hardcoded
> values.  For x86_64, I would suggest 0xfffffffffffe0000 and
> 0xffffffffffff0000.

I see. I assume you are suggesting these values for x86_64 because they
lie in an unused hole. That makes sense to me.

For the case of x86_32, I have trouble finding a suitable place as there
are not many available holes. It could be put before VMALLOC_START or
after VMALLOC_END but this would reveal the position of the vmalloc
area. Although, to my knowledge, randomized memory is not available for
x86_32. Without randomization, does it hurt to make sidt/sgdt return the
address of a kernel static variable?

> 
> >
> > The instructions sldt and str return a segment selector relative to the
> > base address of the global descriptor table. Since the actual address of
> > such table is not revealed, it makes sense to emulate the result as zero.
> 
> Hmm, now I wonder if anything uses SLDT to see if there is an LDT.  If
> so, we could emulate it better, but I doubt this matters.

So you are saying that the emulated sldt should return a different value
based on the presence/absence of a LDT? This could reveal this very
fact.

> 
> >
> > The instruction smsw is emulated to return zero.
> 
> If you're going to emulate it, please return something plausible.  The
> protected mode bit should be on, for example.  0x33 is probably
> reasonable.

Sure. Will do.
> 
> > +static int __emulate_umip_insn(struct insn *insn, enum umip_insn umip_inst,
> > +                              unsigned char *data, int *data_size)
> > +{
> > +       unsigned long const *dummy_base_addr;
> > +       unsigned short dummy_limit = 0;
> > +       unsigned short dummy_value = 0;
> > +
> > +       switch (umip_inst) {
> > +       /*
> > +        * These two instructions return the base address and limit of the
> > +        * global and interrupt descriptor table. The base address can be
> > +        * 32-bit or 64-bit. Limit is always 16-bit.
> > +        */
> > +       case UMIP_SGDT:
> > +       case UMIP_SIDT:
> > +               if (umip_inst == UMIP_SGDT)
> > +                       dummy_base_addr = &umip_dummy_gdt_base;
> > +               else
> > +                       dummy_base_addr = &umip_dummy_idt_base;
> > +               if (X86_MODRM_MOD(insn->modrm.value) == 3) {
> > +                       WARN_ONCE(1, "SGDT cannot take register as argument!\n");
> 
> No warnings please.

I'll. Remove it.
> 
> > +int fixup_umip_exception(struct pt_regs *regs)
> > +{
> > +       struct insn insn;
> > +       unsigned char buf[MAX_INSN_SIZE];
> > +       /* 10 bytes is the maximum size of the result of UMIP instructions */
> > +       unsigned char dummy_data[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
> > +       int x86_64 = !test_thread_flag(TIF_IA32);
> 
> user_64bit_mode(regs)

I'll make this change.
> 
> > +       int not_copied, nr_copied, reg_offset, dummy_data_size;
> > +       void __user *uaddr;
> > +       unsigned long *reg_addr;
> > +       enum umip_insn umip_inst;
> > +
> > +       not_copied = copy_from_user(buf, (void __user *)regs->ip, sizeof(buf));
> 
> This is slightly wrong due to PKRU.  I doubt we care.

I see. If I am not mistaken, if the memory is protected by a protection
key this would cause a page fault. I'll make a note of it.
> 
> > +       nr_copied = sizeof(buf) - not_copied;
> > +       /*
> > +        * The decoder _should_ fail nicely if we pass it a short buffer.
> > +        * But, let's not depend on that implementation detail.  If we
> > +        * did not get anything, just error out now.
> > +        */
> > +       if (!nr_copied)
> > +               return -EFAULT;
> 
> If the caller cares about EINVAL vs EFAULT, it cares because it is
> considering changing the signal to a fake page fault.  If so, then
> this should be EINVAL -- failure to read the text should just prevent
> emulation.

I see. The caller in this case do_general_protection, which will issue a
SIGSEGV to the user space anyways. I don't think it cares about the
EINVAL vs EFAULT. It does care about whether the emulation was
successful.

> 
> > +       insn_init(&insn, buf, nr_copied, x86_64);
> > +       insn_get_length(&insn);
> > +       if (nr_copied < insn.length)
> > +               return -EFAULT;
> 
> Ditto.
I will change to EINVAL.
> 
> > +
> > +       umip_inst = __identify_insn(&insn);
> > +       /* Check if we found an instruction protected by UMIP */
> > +       if (umip_inst < 0)
> > +               return -EINVAL;
> > +
> > +       if (__emulate_umip_insn(&insn, umip_inst, dummy_data, &dummy_data_size))
> > +               return -EINVAL;
> > +
> > +       /* If operand is a register, write directly to it */
> > +       if (X86_MODRM_MOD(insn.modrm.value) == 3) {
> > +               reg_offset = get_reg_offset_rm(&insn, regs);
> > +               reg_addr = (unsigned long *)((unsigned long)regs + reg_offset);
> > +               memcpy(reg_addr, dummy_data, dummy_data_size);
> > +       } else {
> > +               uaddr = insn_get_addr_ref(&insn, regs);
> > +               nr_copied = copy_to_user(uaddr, dummy_data, dummy_data_size);
> > +               if (nr_copied  > 0)
> > +                       return -EFAULT;
> 
> This should be the only EFAULT case.
Should this be EFAULT event if the caller cares only about successful
(return 0) vs failed (return non-0) emulation?

Thanks for your thorough review! I really appreciate it.

Thanks and BR,
Ricardo


  reply	other threads:[~2016-12-28  0:39 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-24  1:37 [v2 0/7] x86: enable User-Mode Instruction Prevention Ricardo Neri
2016-12-24  1:37 ` [v2 1/7] x86/mpx: Do not use SIB index if index points to R/ESP Ricardo Neri
2016-12-24  1:57   ` Andy Lutomirski
2016-12-27 22:29     ` Ricardo Neri
2016-12-24  1:37 ` [v2 2/7] x86/mpx: Fail when implicit zero-displacement is used along with R/EBP Ricardo Neri
2016-12-24  1:58   ` Andy Lutomirski
2016-12-27 22:33     ` Ricardo Neri
2017-01-03 16:41       ` Dave Hansen
2017-01-04  1:31         ` Ricardo Neri
2016-12-24  1:37 ` [v2 3/7] x86/mpx, x86/insn: Relocate insn util functions to a new insn-utils Ricardo Neri
2016-12-24  2:34   ` kbuild test robot
2016-12-25  6:17   ` Masami Hiramatsu
2016-12-27 22:36     ` Ricardo Neri
2017-01-03 16:44   ` Dave Hansen
2017-01-04  1:31     ` Ricardo Neri
2016-12-24  1:37 ` [v2 4/7] x86/cpufeature: Add User-Mode Instruction Prevention definitions Ricardo Neri
2016-12-24  1:37 ` [v2 5/7] x86: Add emulation code for UMIP instructions Ricardo Neri
2016-12-24  2:11   ` Andy Lutomirski
2016-12-28  0:39     ` Ricardo Neri [this message]
2016-12-28  0:48       ` Andy Lutomirski
2016-12-30  5:23         ` Ricardo Neri
2016-12-31  2:07           ` Andy Lutomirski
2017-01-04  1:30             ` Ricardo Neri
2016-12-25 15:49   ` Masami Hiramatsu
2016-12-28  0:40     ` Ricardo Neri
2016-12-24  1:37 ` [v2 6/7] x86/traps: Fixup general protection faults caused by UMIP Ricardo Neri
2016-12-24  2:11   ` Andy Lutomirski
2016-12-27 22:34     ` Ricardo Neri
2016-12-24  4:22   ` kbuild test robot
2016-12-24  1:37 ` [v2 7/7] x86: Enable User-Mode Instruction Prevention Ricardo Neri
2016-12-24  3:15   ` kbuild test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1482885582.106950.29.camel@ranerica-desktop \
    --to=ricardo.neri-calderon@linux.intel.com \
    --cc=akpm@linux-foundation.org \
    --cc=bp@suse.de \
    --cc=brgerst@gmail.com \
    --cc=cmetcalf@mellanox.com \
    --cc=corbet@lwn.net \
    --cc=dave.hansen@linux.intel.com \
    --cc=fenghua.yu@intel.com \
    --cc=hpa@zytor.com \
    --cc=jslaby@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-msdos@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=luto@kernel.org \
    --cc=mingo@redhat.com \
    --cc=mst@redhat.com \
    --cc=paul.gortmaker@windriver.com \
    --cc=peterz@infradead.org \
    --cc=ray.huang@amd.com \
    --cc=slaoub@gmail.com \
    --cc=tglx@linutronix.de \
    --cc=wine-devel@winehq.org \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox