diff mbox series

[v2,05/11] KVM: x86: emulator: update the emulation mode after CR0 write

Message ID 20220621150902.46126-6-mlevitsk@redhat.com (mailing list archive)
State New, archived
Headers show
Series SMM emulation and interrupt shadow fixes | expand

Commit Message

Maxim Levitsky June 21, 2022, 3:08 p.m. UTC
CR0.PE toggles real/protected mode, thus its update
should update the emulation mode.

This is likely a benign bug because there is no writeback
of state, other than the RIP increment, and when toggling
CR0.PE, the CPU has to execute code from a very low memory address.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 arch/x86/kvm/emulate.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

Comments

Sean Christopherson July 20, 2022, 11:50 p.m. UTC | #1
On Tue, Jun 21, 2022, Maxim Levitsky wrote:
> CR0.PE toggles real/protected mode, thus its update
> should update the emulation mode.
> 
> This is likely a benign bug because there is no writeback
> of state, other than the RIP increment, and when toggling
> CR0.PE, the CPU has to execute code from a very low memory address.
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  arch/x86/kvm/emulate.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index 6f4632babc4cd8..002687d17f9364 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -3659,11 +3659,22 @@ static int em_movbe(struct x86_emulate_ctxt *ctxt)
>  
>  static int em_cr_write(struct x86_emulate_ctxt *ctxt)
>  {
> -	if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val))
> +	int cr_num = ctxt->modrm_reg;
> +	int r;
> +
> +	if (ctxt->ops->set_cr(ctxt, cr_num, ctxt->src.val))
>  		return emulate_gp(ctxt, 0);
>  
>  	/* Disable writeback. */
>  	ctxt->dst.type = OP_NONE;
> +
> +	if (cr_num == 0) {
> +		/* CR0 write might have updated CR0.PE */

Or toggled CR0.PG.  It's probably also worth noting that ->set_cr() handles side
effects to other registers, e.g. the lack of an EFER.LMA update makes this look
suspicious at first glance.

> +		r = update_emulation_mode(ctxt);
> +		if (r != X86EMUL_CONTINUE)
> +			return r;
> +	}
> +
>  	return X86EMUL_CONTINUE;
>  }
>  
> -- 
> 2.26.3
>
Maxim Levitsky July 21, 2022, 11:53 a.m. UTC | #2
On Wed, 2022-07-20 at 23:50 +0000, Sean Christopherson wrote:
> On Tue, Jun 21, 2022, Maxim Levitsky wrote:
> > CR0.PE toggles real/protected mode, thus its update
> > should update the emulation mode.
> > 
> > This is likely a benign bug because there is no writeback
> > of state, other than the RIP increment, and when toggling
> > CR0.PE, the CPU has to execute code from a very low memory address.
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  arch/x86/kvm/emulate.c | 13 ++++++++++++-
> >  1 file changed, 12 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> > index 6f4632babc4cd8..002687d17f9364 100644
> > --- a/arch/x86/kvm/emulate.c
> > +++ b/arch/x86/kvm/emulate.c
> > @@ -3659,11 +3659,22 @@ static int em_movbe(struct x86_emulate_ctxt *ctxt)
> >  
> >  static int em_cr_write(struct x86_emulate_ctxt *ctxt)
> >  {
> > -	if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val))
> > +	int cr_num = ctxt->modrm_reg;
> > +	int r;
> > +
> > +	if (ctxt->ops->set_cr(ctxt, cr_num, ctxt->src.val))
> >  		return emulate_gp(ctxt, 0);
> >  
> >  	/* Disable writeback. */
> >  	ctxt->dst.type = OP_NONE;
> > +
> > +	if (cr_num == 0) {
> > +		/* CR0 write might have updated CR0.PE */
> 
> Or toggled CR0.PG.  

I thought about it but paging actually does not affect the CPU mode.

E.g if you are in protected mode, instructions execute the same regardless
if you have paging or not.

(There are probably some exceptions but you understand what I mean).

Best regards,
	Maxim Levitsky

> It's probably also worth noting that ->set_cr() handles side
> effects to other registers, e.g. the lack of an EFER.LMA update makes this look
> suspicious at first glance.

> 
> > +		r = update_emulation_mode(ctxt);
> > +		if (r != X86EMUL_CONTINUE)
> > +			return r;
> > +	}
> > +
> >  	return X86EMUL_CONTINUE;
> >  }
> >  
> > -- 
> > 2.26.3
> >
Sean Christopherson July 21, 2022, 2:11 p.m. UTC | #3
On Thu, Jul 21, 2022, Maxim Levitsky wrote:
> On Wed, 2022-07-20 at 23:50 +0000, Sean Christopherson wrote:
> > On Tue, Jun 21, 2022, Maxim Levitsky wrote:
> > > CR0.PE toggles real/protected mode, thus its update
> > > should update the emulation mode.
> > > 
> > > This is likely a benign bug because there is no writeback
> > > of state, other than the RIP increment, and when toggling
> > > CR0.PE, the CPU has to execute code from a very low memory address.
> > > 
> > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > ---
> > >  arch/x86/kvm/emulate.c | 13 ++++++++++++-
> > >  1 file changed, 12 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> > > index 6f4632babc4cd8..002687d17f9364 100644
> > > --- a/arch/x86/kvm/emulate.c
> > > +++ b/arch/x86/kvm/emulate.c
> > > @@ -3659,11 +3659,22 @@ static int em_movbe(struct x86_emulate_ctxt *ctxt)
> > >  
> > >  static int em_cr_write(struct x86_emulate_ctxt *ctxt)
> > >  {
> > > -	if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val))
> > > +	int cr_num = ctxt->modrm_reg;
> > > +	int r;
> > > +
> > > +	if (ctxt->ops->set_cr(ctxt, cr_num, ctxt->src.val))
> > >  		return emulate_gp(ctxt, 0);
> > >  
> > >  	/* Disable writeback. */
> > >  	ctxt->dst.type = OP_NONE;
> > > +
> > > +	if (cr_num == 0) {
> > > +		/* CR0 write might have updated CR0.PE */
> > 
> > Or toggled CR0.PG.  
> 
> I thought about it but paging actually does not affect the CPU mode.

Toggling CR0.PG when EFER.LME=1 (and CR4.PAE=1) switches the CPU in and out of
long mode.  That's why I mentioned the EFER.LMA thing below.  It's also notable
in that the only reason we don't have to handle CR4 here is because clearing
CR4.PAE while long is active causes a #GP.  
 
> E.g if you are in protected mode, instructions execute the same regardless
> if you have paging or not.
> 
> (There are probably some exceptions but you understand what I mean).
> 
> Best regards,
> 	Maxim Levitsky
> 
> > It's probably also worth noting that ->set_cr() handles side
> > effects to other registers, e.g. the lack of an EFER.LMA update makes this look
> > suspicious at first glance.
Maxim Levitsky July 21, 2022, 2:57 p.m. UTC | #4
On Thu, 2022-07-21 at 14:11 +0000, Sean Christopherson wrote:
> On Thu, Jul 21, 2022, Maxim Levitsky wrote:
> > On Wed, 2022-07-20 at 23:50 +0000, Sean Christopherson wrote:
> > > On Tue, Jun 21, 2022, Maxim Levitsky wrote:
> > > > CR0.PE toggles real/protected mode, thus its update
> > > > should update the emulation mode.
> > > > 
> > > > This is likely a benign bug because there is no writeback
> > > > of state, other than the RIP increment, and when toggling
> > > > CR0.PE, the CPU has to execute code from a very low memory address.
> > > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  arch/x86/kvm/emulate.c | 13 ++++++++++++-
> > > >  1 file changed, 12 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> > > > index 6f4632babc4cd8..002687d17f9364 100644
> > > > --- a/arch/x86/kvm/emulate.c
> > > > +++ b/arch/x86/kvm/emulate.c
> > > > @@ -3659,11 +3659,22 @@ static int em_movbe(struct x86_emulate_ctxt *ctxt)
> > > >  
> > > >  static int em_cr_write(struct x86_emulate_ctxt *ctxt)
> > > >  {
> > > > -	if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val))
> > > > +	int cr_num = ctxt->modrm_reg;
> > > > +	int r;
> > > > +
> > > > +	if (ctxt->ops->set_cr(ctxt, cr_num, ctxt->src.val))
> > > >  		return emulate_gp(ctxt, 0);
> > > >  
> > > >  	/* Disable writeback. */
> > > >  	ctxt->dst.type = OP_NONE;
> > > > +
> > > > +	if (cr_num == 0) {
> > > > +		/* CR0 write might have updated CR0.PE */
> > > 
> > > Or toggled CR0.PG.  
> > 
> > I thought about it but paging actually does not affect the CPU mode.
> 
> Toggling CR0.PG when EFER.LME=1 (and CR4.PAE=1) switches the CPU in and out of
> long mode.  That's why I mentioned the EFER.LMA thing below.  It's also notable
> in that the only reason we don't have to handle CR4 here is because clearing
> CR4.PAE while long is active causes a #GP.  

I had a distinct feeling that this is related to LMA/LME which I always learn and then forget
Now I do, and I wrote a short summary for myself to refresh my memory when I forget about this again :-)

I'll update the comment again in v3.

Thanks a lot,
	Best regards,
		Maxim Levitsky

>  
> > E.g if you are in protected mode, instructions execute the same regardless
> > if you have paging or not.
> > 
> > (There are probably some exceptions but you understand what I mean).
> > 
> > Best regards,
> > 	Maxim Levitsky
> > 
> > > It's probably also worth noting that ->set_cr() handles side
> > > effects to other registers, e.g. the lack of an EFER.LMA update makes this look
> > > suspicious at first glance.
diff mbox series

Patch

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 6f4632babc4cd8..002687d17f9364 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -3659,11 +3659,22 @@  static int em_movbe(struct x86_emulate_ctxt *ctxt)
 
 static int em_cr_write(struct x86_emulate_ctxt *ctxt)
 {
-	if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val))
+	int cr_num = ctxt->modrm_reg;
+	int r;
+
+	if (ctxt->ops->set_cr(ctxt, cr_num, ctxt->src.val))
 		return emulate_gp(ctxt, 0);
 
 	/* Disable writeback. */
 	ctxt->dst.type = OP_NONE;
+
+	if (cr_num == 0) {
+		/* CR0 write might have updated CR0.PE */
+		r = update_emulation_mode(ctxt);
+		if (r != X86EMUL_CONTINUE)
+			return r;
+	}
+
 	return X86EMUL_CONTINUE;
 }