lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-ID: <017887cf42991777c96949ecf81469965113b412.1725622408.git.legion@kernel.org> Date: Fri, 6 Sep 2024 13:50:04 +0200 From: Alexey Gladkov <legion@...nel.org> To: linux-kernel@...r.kernel.org, linux-coco@...ts.linux.dev Cc: "Alexey Gladkov (Intel)" <legion@...nel.org>, Thomas Gleixner <tglx@...utronix.de>, Ingo Molnar <mingo@...hat.com>, Borislav Petkov <bp@...en8.de>, Dave Hansen <dave.hansen@...ux.intel.com>, "H. Peter Anvin" <hpa@...or.com>, "Kirill A. Shutemov" <kirill.shutemov@...ux.intel.com>, Andrew Morton <akpm@...ux-foundation.org>, Yuan Yao <yuan.yao@...el.com>, Geert Uytterhoeven <geert@...ux-m68k.org>, Yuntao Wang <ytcoode@...il.com>, Kai Huang <kai.huang@...el.com>, Baoquan He <bhe@...hat.com>, Oleg Nesterov <oleg@...hat.com>, cho@...rosoft.com, decui@...rosoft.com, John.Starks@...rosoft.com Subject: [PATCH v6 6/6] x86/tdx: Implement MOVS for MMIO From: "Alexey Gladkov (Intel)" <legion@...nel.org> Add emulation of the MOVS instruction on MMIO regions. MOVS emulation consists of dividing it into a series of read and write operations, which in turn will be validated separately. This implementation is based on the same principle as in SEV. It splits MOVS into separate read and write operations, which in turn can cause nested #VEs depending on which of the arguments caused the first #VE. The difference with the SEV implementation is the execution context. SEV code is executed in atomic context. Exception handler in TDX is executed with interrupts enabled. That's why the approach to locking is different. In TDX, mmap_lock is taken to verify and emulate the instruction. Another difference is how the read and write instructions are executed for MOVS emulation. While in SEV each read/write operation returns to user space, in TDX these operations are performed from the kernel context. It may be possible to achieve more code reuse at this point, but it would require confirmation from SEV that such a thing wouldn't break anything. Signed-off-by: Alexey Gladkov (Intel) <legion@...nel.org> --- arch/x86/coco/tdx/tdx.c | 83 +++++++++++++++++++++++++++++--- arch/x86/include/asm/processor.h | 3 ++ 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 1e391897e34f..7e760f03fa1e 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -518,6 +518,60 @@ static int decode_insn_struct(struct insn *insn, struct pt_regs *regs) return 0; } +static int handle_mmio_movs(struct insn *insn, struct pt_regs *regs, int size, struct ve_info *ve) +{ + unsigned long ds_base, es_base; + unsigned char *src, *dst; + unsigned char buffer[8]; + int off, ret; + bool rep; + + /* + * The in-kernel code must use a special API that does not use MOVS. + * If the MOVS instruction is received from in-kernel, then something + * is broken. + */ + if (WARN_ON_ONCE(!user_mode(regs))) + return -EFAULT; + + ds_base = insn_get_seg_base(regs, INAT_SEG_REG_DS); + es_base = insn_get_seg_base(regs, INAT_SEG_REG_ES); + + if (ds_base == -1L || es_base == -1L) + return -EINVAL; + + current->thread.in_mmio_emul = 1; + + rep = insn_has_rep_prefix(insn); + + do { + src = ds_base + (unsigned char *) regs->si; + dst = es_base + (unsigned char *) regs->di; + + ret = __get_iomem(src, buffer, size); + if (ret) + goto out; + + ret = __put_iomem(dst, buffer, size); + if (ret) + goto out; + + off = (regs->flags & X86_EFLAGS_DF) ? -size : size; + + regs->si += off; + regs->di += off; + + if (rep) + regs->cx -= 1; + } while (rep || regs->cx > 0); + + ret = insn->length; +out: + current->thread.in_mmio_emul = 0; + + return ret; +} + static int handle_mmio_write(struct insn *insn, enum insn_mmio_type mmio, int size, struct pt_regs *regs, struct ve_info *ve) { @@ -539,9 +593,8 @@ static int handle_mmio_write(struct insn *insn, enum insn_mmio_type mmio, int si return insn->length; case INSN_MMIO_MOVS: /* - * MMIO was accessed with an instruction that could not be - * decoded or handled properly. It was likely not using io.h - * helpers or accessed MMIO accidentally. + * MOVS is processed through higher level emulation which breaks + * this instruction into a sequence of reads and writes. */ return -EINVAL; default: @@ -600,6 +653,7 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve) { enum insn_mmio_type mmio; struct insn insn = {}; + int need_validation; unsigned long vaddr; int size, ret; @@ -611,13 +665,27 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve) if (WARN_ON_ONCE(mmio == INSN_MMIO_DECODE_FAILED)) return -EINVAL; + if (mmio == INSN_MMIO_MOVS) + return handle_mmio_movs(&insn, regs, size, ve); + + need_validation = user_mode(regs); + if (!user_mode(regs) && !is_kernel_addr(ve->gla)) { - WARN_ONCE(1, "Access to userspace address is not supported"); - return -EINVAL; + /* + * Access from kernel to userspace addresses is not allowed + * unless it is a nested exception during MOVS emulation. + */ + if (!current->thread.in_mmio_emul || !current->mm) { + WARN_ONCE(1, "Access to userspace address is not supported"); + return -EINVAL; + } + + need_validation = 1; + } vaddr = (unsigned long)insn_get_addr_ref(&insn, regs); - if (user_mode(regs)) { + if (need_validation) { if (mmap_read_lock_killable(current->mm)) return -EINTR; @@ -643,7 +711,6 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve) switch (mmio) { case INSN_MMIO_WRITE: case INSN_MMIO_WRITE_IMM: - case INSN_MMIO_MOVS: ret = handle_mmio_write(&insn, mmio, size, regs, ve); break; case INSN_MMIO_READ: @@ -664,7 +731,7 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve) ret = -EINVAL; } unlock: - if (user_mode(regs)) + if (need_validation) mmap_read_unlock(current->mm); return ret; diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index a75a07f4931f..33875a217ed8 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -486,6 +486,9 @@ struct thread_struct { unsigned long iopl_emul; unsigned int iopl_warn:1; +#ifdef CONFIG_INTEL_TDX_GUEST + unsigned int in_mmio_emul:1; +#endif /* * Protection Keys Register for Userspace. Loaded immediately on -- 2.46.0
Powered by blists - more mailing lists