All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: Sean Christopherson <seanjc@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Vitaly Kuznetsov <vkuznets@redhat.com>,
	Wanpeng Li <wanpengli@tencent.com>,
	Jim Mattson <jmattson@google.com>,
	x86@kernel.org, "H. Peter Anvin" <hpa@zytor.com>,
	kvm@vger.kernel.org, Joerg Roedel <joro@8bytes.org>,
	linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org
Subject: Re: [PATCH] KVM: x86/emulator: Bounds check reg nr against reg array size
Date: Fri, 20 May 2022 11:19:19 -0700	[thread overview]
Message-ID: <202205201115.5E830F0@keescook> (raw)
In-Reply-To: <YofQlBrlx18J7h9Y@google.com>

On Fri, May 20, 2022 at 05:32:04PM +0000, Sean Christopherson wrote:
> On Fri, May 20, 2022, Kees Cook wrote:
> > GCC 12 sees that it might be possible for "nr" to be outside the _regs
> > array. Add explicit bounds checking.
> 
> I think GCC 12 is wrong.

I think it's more like GCC is extremely conservative about these things,
and assumes the worst when, for whatever reason, it can't track
something.

> There are four uses of reg_rmw() that don't use hardcoded registers:
> 
>    $ git grep reg_rmw | grep -v VCPU_REGS_
>    emulate.c:static ulong *reg_rmw(struct x86_emulate_ctxt *ctxt, unsigned nr)
> 1  emulate.c:	ulong *preg = reg_rmw(ctxt, reg);
> 2  emulate.c:		p = (unsigned char *)reg_rmw(ctxt, modrm_reg & 3) + 1;
> 3  emulate.c:		p = reg_rmw(ctxt, modrm_reg);
> 4  emulate.c:		assign_register(reg_rmw(ctxt, reg), val, ctxt->op_bytes);
> 
> #1 has three users, but two of those use hardcoded registers.
> 
>   $ git grep register_address_increment | grep -v VCPU_REGS_
>   emulate.c:register_address_increment(struct x86_emulate_ctxt *ctxt, int reg, int inc)
>   emulate.c:	register_address_increment(ctxt, reg, df * op->bytes);
>  
> and that last one is string_addr_inc(), which is only called with RDI or RSI.
> 
> #2 can't overflow as the register can only be 0-3 (yay AH/BH/CH/DH operands).
> 
> #3 is the !highbyte path of decode_register(), and is a bit messy, but modrm_reg
> is always sanitized.
> 
>    $ git grep -E "decode_register\("
>    emulate.c:static void *decode_register(struct x86_emulate_ctxt *ctxt, u8 modrm_reg,
> a  emulate.c:      op->addr.reg = decode_register(ctxt, reg, ctxt->d & ByteOp);
> b  emulate.c:              op->addr.reg = decode_register(ctxt, ctxt->modrm_rm,
> c  emulate.c:                      ctxt->memop.addr.reg = decode_register(ctxt,
>                                                                           ctxt->modrm_rm, true);
> 
> For (b) and (c), modrm_reg == ctxt->modrm_rm, which is computed in one place and
> is bounded to 0-15:
> 
> 	base_reg = (ctxt->rex_prefix << 3) & 8; /* REX.B */
> 	ctxt->modrm_rm = base_reg | (ctxt->modrm & 0x07);
> 
> For (a), "reg" is either modrm_reg or a register that is encoded in the opcode,
> both of which are again bounded to 0-15:
> 
> 	unsigned reg = ctxt->modrm_reg;
> 
> 	if (!(ctxt->d & ModRM))
> 		reg = (ctxt->b & 7) | ((ctxt->rex_prefix & 1) << 3);
> 
> and
> 
> 	ctxt->modrm_reg = ((ctxt->rex_prefix << 1) & 8); /* REX.R */
> 	ctxt->modrm_reg |= (ctxt->modrm & 0x38) >> 3;
> 
> #4 is em_popa() and is just funky hardcoding of popping RAX-RDI, minus RSP.
> 
> I did the same exercise for reg_reg() and write_reg(), and the handful of
> non-hardcoded use are all bounded in similar ways.

Thanks for digging into this. I tried to do the same and started to lose
track of things.

> 
> > In function 'reg_read',
> >     inlined from 'reg_rmw' at ../arch/x86/kvm/emulate.c:266:2:
> 
> Is there more of the "stack" available?  I don't mind the WARN too much, but if
> there is a bug lurking I would much rather fix the bug.

Agreed, but I haven't found a way to get more context here. I think I
found a separate place where GCC really does look to have a bug, as it
complains about array usage that is explicitly bounded. :P

-- 
Kees Cook

  reply	other threads:[~2022-05-20 18:19 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-20 16:57 [PATCH] KVM: x86/emulator: Bounds check reg nr against reg array size Kees Cook
2022-05-20 17:32 ` Sean Christopherson
2022-05-20 18:19   ` Kees Cook [this message]
2022-05-20 18:48 ` Sean Christopherson

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=202205201115.5E830F0@keescook \
    --to=keescook@chromium.org \
    --cc=hpa@zytor.com \
    --cc=jmattson@google.com \
    --cc=joro@8bytes.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=vkuznets@redhat.com \
    --cc=wanpengli@tencent.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.