x86/sev: Check IOBM for IOIO exceptions from user-space
authorJoerg Roedel <jroedel@suse.de>
Wed, 21 Jun 2023 15:42:42 +0000 (17:42 +0200)
committerBorislav Petkov (AMD) <bp@alien8.de>
Mon, 9 Oct 2023 13:47:57 +0000 (15:47 +0200)
Check the IO permission bitmap (if present) before emulating IOIO #VC
exceptions for user-space. These permissions are checked by hardware
already before the #VC is raised, but due to the VC-handler decoding
race it needs to be checked again in software.

Fixes: 25189d08e516 ("x86/sev-es: Add support for handling IOIO exceptions")
Reported-by: Tom Dohrmann <erbse.13@gmx.de>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Tested-by: Tom Dohrmann <erbse.13@gmx.de>
Cc: <stable@kernel.org>
arch/x86/boot/compressed/sev.c
arch/x86/kernel/sev-shared.c
arch/x86/kernel/sev.c

index dc8c876fbd8f8f51c53a26237094dfd6580c8fe2..afd91041ec3ed062a7ce1c68c443b7f1650dfebe 100644 (file)
@@ -103,6 +103,11 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
        return ES_OK;
 }
 
+static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
+{
+       return ES_OK;
+}
+
 #undef __init
 #define __init
 
index 2eabccde94fb31b77b738e4b32d614fd35b5b5c8..bcd77c48be0a5aa18aaf4f81f502d02c6a9500c2 100644 (file)
@@ -656,6 +656,9 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
 static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
 {
        struct insn *insn = &ctxt->insn;
+       size_t size;
+       u64 port;
+
        *exitinfo = 0;
 
        switch (insn->opcode.bytes[0]) {
@@ -664,7 +667,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
        case 0x6d:
                *exitinfo |= IOIO_TYPE_INS;
                *exitinfo |= IOIO_SEG_ES;
-               *exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
+               port       = ctxt->regs->dx & 0xffff;
                break;
 
        /* OUTS opcodes */
@@ -672,41 +675,43 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
        case 0x6f:
                *exitinfo |= IOIO_TYPE_OUTS;
                *exitinfo |= IOIO_SEG_DS;
-               *exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
+               port       = ctxt->regs->dx & 0xffff;
                break;
 
        /* IN immediate opcodes */
        case 0xe4:
        case 0xe5:
                *exitinfo |= IOIO_TYPE_IN;
-               *exitinfo |= (u8)insn->immediate.value << 16;
+               port       = (u8)insn->immediate.value & 0xffff;
                break;
 
        /* OUT immediate opcodes */
        case 0xe6:
        case 0xe7:
                *exitinfo |= IOIO_TYPE_OUT;
-               *exitinfo |= (u8)insn->immediate.value << 16;
+               port       = (u8)insn->immediate.value & 0xffff;
                break;
 
        /* IN register opcodes */
        case 0xec:
        case 0xed:
                *exitinfo |= IOIO_TYPE_IN;
-               *exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
+               port       = ctxt->regs->dx & 0xffff;
                break;
 
        /* OUT register opcodes */
        case 0xee:
        case 0xef:
                *exitinfo |= IOIO_TYPE_OUT;
-               *exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
+               port       = ctxt->regs->dx & 0xffff;
                break;
 
        default:
                return ES_DECODE_FAILED;
        }
 
+       *exitinfo |= port << 16;
+
        switch (insn->opcode.bytes[0]) {
        case 0x6c:
        case 0x6e:
@@ -716,12 +721,15 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
        case 0xee:
                /* Single byte opcodes */
                *exitinfo |= IOIO_DATA_8;
+               size       = 1;
                break;
        default:
                /* Length determined by instruction parsing */
                *exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
                                                     : IOIO_DATA_32;
+               size       = (insn->opnd_bytes == 2) ? 2 : 4;
        }
+
        switch (insn->addr_bytes) {
        case 2:
                *exitinfo |= IOIO_ADDR_16;
@@ -737,7 +745,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
        if (insn_has_rep_prefix(insn))
                *exitinfo |= IOIO_REP;
 
-       return ES_OK;
+       return vc_ioio_check(ctxt, (u16)port, size);
 }
 
 static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
index 4ee14e647ae62b0efc8c15196e57dbcd466c3532..0f218932e844c5e073cb600d4c1a5aec3c3f3b98 100644 (file)
@@ -524,6 +524,33 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt
        return ES_OK;
 }
 
+static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
+{
+       BUG_ON(size > 4);
+
+       if (user_mode(ctxt->regs)) {
+               struct thread_struct *t = &current->thread;
+               struct io_bitmap *iobm = t->io_bitmap;
+               size_t idx;
+
+               if (!iobm)
+                       goto fault;
+
+               for (idx = port; idx < port + size; ++idx) {
+                       if (test_bit(idx, iobm->bitmap))
+                               goto fault;
+               }
+       }
+
+       return ES_OK;
+
+fault:
+       ctxt->fi.vector = X86_TRAP_GP;
+       ctxt->fi.error_code = 0;
+
+       return ES_EXCEPTION;
+}
+
 /* Include code shared with pre-decompression boot stage */
 #include "sev-shared.c"