Skip to content

Commit 0df072a

Browse files
committed
Merge tag 'sev_fixes_for_v6.6' of //git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fixes from Borislav Petkov: "Take care of a race between when the #VC exception is raised and when the guest kernel gets to emulate certain instructions in SEV-{ES,SNP} guests by: - disabling emulation of MMIO instructions when coming from user mode - checking the IO permission bitmap before emulating IO instructions and verifying the memory operands of INS/OUTS insns" * tag 'sev_fixes_for_v6.6' of //git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/sev: Check for user-space IOIO pointing to kernel space x86/sev: Check IOBM for IOIO exceptions from user-space x86/sev: Disable MMIO emulation from user mode
2 parents ce55c22 + 63e44bc commit 0df072a

File tree

3 files changed

+84
-9
lines changed

3 files changed

+84
-9
lines changed

arch/x86/boot/compressed/sev.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
103103
return ES_OK;
104104
}
105105

106+
static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
107+
{
108+
return ES_OK;
109+
}
110+
111+
static bool fault_in_kernel_space(unsigned long address)
112+
{
113+
return false;
114+
}
115+
106116
#undef __init
107117
#define __init
108118

arch/x86/kernel/sev-shared.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -632,14 +632,36 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code)
632632
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
633633
}
634634

635+
static enum es_result vc_insn_string_check(struct es_em_ctxt *ctxt,
636+
unsigned long address,
637+
bool write)
638+
{
639+
if (user_mode(ctxt->regs) && fault_in_kernel_space(address)) {
640+
ctxt->fi.vector = X86_TRAP_PF;
641+
ctxt->fi.error_code = X86_PF_USER;
642+
ctxt->fi.cr2 = address;
643+
if (write)
644+
ctxt->fi.error_code |= X86_PF_WRITE;
645+
646+
return ES_EXCEPTION;
647+
}
648+
649+
return ES_OK;
650+
}
651+
635652
static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt,
636653
void *src, char *buf,
637654
unsigned int data_size,
638655
unsigned int count,
639656
bool backwards)
640657
{
641658
int i, b = backwards ? -1 : 1;
642-
enum es_result ret = ES_OK;
659+
unsigned long address = (unsigned long)src;
660+
enum es_result ret;
661+
662+
ret = vc_insn_string_check(ctxt, address, false);
663+
if (ret != ES_OK)
664+
return ret;
643665

644666
for (i = 0; i < count; i++) {
645667
void *s = src + (i * data_size * b);
@@ -660,7 +682,12 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
660682
bool backwards)
661683
{
662684
int i, s = backwards ? -1 : 1;
663-
enum es_result ret = ES_OK;
685+
unsigned long address = (unsigned long)dst;
686+
enum es_result ret;
687+
688+
ret = vc_insn_string_check(ctxt, address, true);
689+
if (ret != ES_OK)
690+
return ret;
664691

665692
for (i = 0; i < count; i++) {
666693
void *d = dst + (i * data_size * s);
@@ -696,6 +723,9 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
696723
static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
697724
{
698725
struct insn *insn = &ctxt->insn;
726+
size_t size;
727+
u64 port;
728+
699729
*exitinfo = 0;
700730

701731
switch (insn->opcode.bytes[0]) {
@@ -704,49 +734,51 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
704734
case 0x6d:
705735
*exitinfo |= IOIO_TYPE_INS;
706736
*exitinfo |= IOIO_SEG_ES;
707-
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
737+
port = ctxt->regs->dx & 0xffff;
708738
break;
709739

710740
/* OUTS opcodes */
711741
case 0x6e:
712742
case 0x6f:
713743
*exitinfo |= IOIO_TYPE_OUTS;
714744
*exitinfo |= IOIO_SEG_DS;
715-
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
745+
port = ctxt->regs->dx & 0xffff;
716746
break;
717747

718748
/* IN immediate opcodes */
719749
case 0xe4:
720750
case 0xe5:
721751
*exitinfo |= IOIO_TYPE_IN;
722-
*exitinfo |= (u8)insn->immediate.value << 16;
752+
port = (u8)insn->immediate.value & 0xffff;
723753
break;
724754

725755
/* OUT immediate opcodes */
726756
case 0xe6:
727757
case 0xe7:
728758
*exitinfo |= IOIO_TYPE_OUT;
729-
*exitinfo |= (u8)insn->immediate.value << 16;
759+
port = (u8)insn->immediate.value & 0xffff;
730760
break;
731761

732762
/* IN register opcodes */
733763
case 0xec:
734764
case 0xed:
735765
*exitinfo |= IOIO_TYPE_IN;
736-
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
766+
port = ctxt->regs->dx & 0xffff;
737767
break;
738768

739769
/* OUT register opcodes */
740770
case 0xee:
741771
case 0xef:
742772
*exitinfo |= IOIO_TYPE_OUT;
743-
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
773+
port = ctxt->regs->dx & 0xffff;
744774
break;
745775

746776
default:
747777
return ES_DECODE_FAILED;
748778
}
749779

780+
*exitinfo |= port << 16;
781+
750782
switch (insn->opcode.bytes[0]) {
751783
case 0x6c:
752784
case 0x6e:
@@ -756,12 +788,15 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
756788
case 0xee:
757789
/* Single byte opcodes */
758790
*exitinfo |= IOIO_DATA_8;
791+
size = 1;
759792
break;
760793
default:
761794
/* Length determined by instruction parsing */
762795
*exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
763796
: IOIO_DATA_32;
797+
size = (insn->opnd_bytes == 2) ? 2 : 4;
764798
}
799+
765800
switch (insn->addr_bytes) {
766801
case 2:
767802
*exitinfo |= IOIO_ADDR_16;
@@ -777,7 +812,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
777812
if (insn_has_rep_prefix(insn))
778813
*exitinfo |= IOIO_REP;
779814

780-
return ES_OK;
815+
return vc_ioio_check(ctxt, (u16)port, size);
781816
}
782817

783818
static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)

arch/x86/kernel/sev.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,33 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt
524524
return ES_OK;
525525
}
526526

527+
static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
528+
{
529+
BUG_ON(size > 4);
530+
531+
if (user_mode(ctxt->regs)) {
532+
struct thread_struct *t = &current->thread;
533+
struct io_bitmap *iobm = t->io_bitmap;
534+
size_t idx;
535+
536+
if (!iobm)
537+
goto fault;
538+
539+
for (idx = port; idx < port + size; ++idx) {
540+
if (test_bit(idx, iobm->bitmap))
541+
goto fault;
542+
}
543+
}
544+
545+
return ES_OK;
546+
547+
fault:
548+
ctxt->fi.vector = X86_TRAP_GP;
549+
ctxt->fi.error_code = 0;
550+
551+
return ES_EXCEPTION;
552+
}
553+
527554
/* Include code shared with pre-decompression boot stage */
528555
#include "sev-shared.c"
529556

@@ -1508,6 +1535,9 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
15081535
return ES_DECODE_FAILED;
15091536
}
15101537

1538+
if (user_mode(ctxt->regs))
1539+
return ES_UNSUPPORTED;
1540+
15111541
switch (mmio) {
15121542
case INSN_MMIO_WRITE:
15131543
memcpy(ghcb->shared_buffer, reg_data, bytes);

0 commit comments

Comments
 (0)