All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Kees Cook <keescook@chromium.org>
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 17:32:04 +0000	[thread overview]
Message-ID: <YofQlBrlx18J7h9Y@google.com> (raw)
In-Reply-To: <20220520165705.2140042-1-keescook@chromium.org>

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.

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.

> 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.

> ../arch/x86/kvm/emulate.c:254:27: warning: array subscript 32 is above array bounds of 'long unsigned int[17]' [-Warray-bounds]
>   254 |         return ctxt->_regs[nr];
>       |                ~~~~~~~~~~~^~~~
> In file included from ../arch/x86/kvm/emulate.c:23:
> ../arch/x86/kvm/kvm_emulate.h: In function 'reg_rmw':
> ../arch/x86/kvm/kvm_emulate.h:366:23: note: while referencing '_regs'
>   366 |         unsigned long _regs[NR_VCPU_REGS];
>       |                       ^~~~~

  reply	other threads:[~2022-05-20 17:32 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 [this message]
2022-05-20 18:19   ` Kees Cook
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=YofQlBrlx18J7h9Y@google.com \
    --to=seanjc@google.com \
    --cc=hpa@zytor.com \
    --cc=jmattson@google.com \
    --cc=joro@8bytes.org \
    --cc=keescook@chromium.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.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.