From: Tim Merrifield <hidden> Date: 2024-07-26 18:49:14
Hypercall instructions like VMCALL and VMMCALL are not restricted to CPL 0.
This allows userspace software like open-vm-tools to communicate directly
with the VMM.
For TDX VMs, this communication may violate the security model. Today,
VMCALLs are not forwarded to the host VMM, which breaks open-vm-tools
and any other userspace software that uses VMCALL.
But if userspace is aware of the risks and has been hardened to
address any known violations of the security model, then it seems
reasonable to allow hypercalls from this process to proceed.
This patchset introduces a new x86 process control flag to address this
concern. By setting the MM_CONTEXT_COCO_USER_HCALL flag, the process opts
in to user-level hypercalls. When TDX is enabled, the VMCALL will #VE and
control will be transferred to a hypervisor-specific hypercall handler
(similar to how things work today for SEV with
sev_es_hcall_prepare/sev_es_hcall_finish). The flag has no effect on
non-TDX VMs. Other confidential computing technologies could use this flag
to provide limited access to user-level hypercalls.
v1->v2 changes:
- Updated coverletter to get to the point a little faster.
- Patch 1: Changed to use a per-process flag rather than a per-thread
flag, based on feedback from Kirill Shutemov. I believe this also addresses
the issue of inheritance raised by Dave Hansen.
- Patch 1: Refactored the logic in tdx.c to be made more clear. Also,
tdx_hcall now returns an error code. Both suggested by Kirill.
- Patch 2: We now zero tdx_module_args to prevent data leakage to the VMM,
pointed out by Kirill.
Tim Merrifield (2):
Add prctl to allow userlevel TDX hypercalls
x86/vmware: VMware support for TDX userspace hypercalls
arch/x86/coco/tdx/tdx.c | 23 ++++++++++++++
arch/x86/include/asm/mmu.h | 2 ++
arch/x86/include/asm/x86_init.h | 1 +
arch/x86/include/uapi/asm/prctl.h | 3 ++
arch/x86/kernel/cpu/vmware.c | 51 ++++++++++++++++++++++++-------
arch/x86/kernel/process.c | 22 +++++++++++++
6 files changed, 91 insertions(+), 11 deletions(-)
--
2.40.1
From: Tim Merrifield <hidden> Date: 2024-07-26 18:49:21
Add a new process-level prctl option to enable/disable user-level
hypercalls when running in a confidential VM. Add support for
checking this flag on VMCALL #VE for TDX and transfer control to
a hypervisor vendor-specific handler.
Signed-off-by: Tim Merrifield <redacted>
---
arch/x86/coco/tdx/tdx.c | 23 +++++++++++++++++++++++
arch/x86/include/asm/mmu.h | 2 ++
arch/x86/include/asm/x86_init.h | 1 +
arch/x86/include/uapi/asm/prctl.h | 3 +++
arch/x86/kernel/process.c | 22 ++++++++++++++++++++++
5 files changed, 51 insertions(+)
@@ -238,6 +238,7 @@ static int ve_instr_len(struct ve_info *ve)caseEXIT_REASON_MSR_WRITE:caseEXIT_REASON_CPUID:caseEXIT_REASON_IO_INSTRUCTION:+caseEXIT_REASON_VMCALL:/* It is safe to use ve->instr_len for #VE due instructions */returnve->instr_len;caseEXIT_REASON_EPT_VIOLATION:
@@ -1042,6 +1042,24 @@ unsigned long __get_wchan(struct task_struct *p)returnaddr;}+staticintget_coco_user_hcall_mode(void)+{+return!test_bit(MM_CONTEXT_COCO_USER_HCALL,+¤t->mm->context.flags);+}++staticintset_coco_user_hcall_mode(unsignedlongenabled)+{+if(enabled)+set_bit(MM_CONTEXT_COCO_USER_HCALL,+¤t->mm->context.flags);+else+clear_bit(MM_CONTEXT_COCO_USER_HCALL,+¤t->mm->context.flags);++return0;+}+longdo_arch_prctl_common(intoption,unsignedlongarg2){switch(option){
@@ -1055,6 +1073,10 @@ long do_arch_prctl_common(int option, unsigned long arg2)caseARCH_GET_XCOMP_GUEST_PERM:caseARCH_REQ_XCOMP_GUEST_PERM:returnfpu_xstate_prctl(option,arg2);+caseARCH_GET_COCO_USER_HCALL:+returnget_coco_user_hcall_mode();+caseARCH_SET_COCO_USER_HCALL:+returnset_coco_user_hcall_mode(arg2);}return-EINVAL;
arch/x86/kernel/process.c:1041:46: error: 'mm_context_t' has no member named 'flags'
1041 | ¤t->mm->context.flags);
| ^
include/linux/bitops.h:45:44: note: in definition of macro 'bitop'
45 | __builtin_constant_p((uintptr_t)(addr) != (uintptr_t)NULL) && \
| ^~~~
arch/x86/kernel/process.c:1040:17: note: in expansion of macro 'test_bit'
1040 | return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
| ^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:46: error: 'mm_context_t' has no member named 'flags'
1041 | ¤t->mm->context.flags);
| ^
include/linux/bitops.h:46:23: note: in definition of macro 'bitop'
46 | (uintptr_t)(addr) != (uintptr_t)NULL && \
| ^~~~
arch/x86/kernel/process.c:1040:17: note: in expansion of macro 'test_bit'
1040 | return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
| ^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:46: error: 'mm_context_t' has no member named 'flags'
1041 | ¤t->mm->context.flags);
| ^
include/linux/bitops.h:47:57: note: in definition of macro 'bitop'
47 | __builtin_constant_p(*(const unsigned long *)(addr))) ? \
| ^~~~
arch/x86/kernel/process.c:1040:17: note: in expansion of macro 'test_bit'
1040 | return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
| ^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:46: error: 'mm_context_t' has no member named 'flags'
1041 | ¤t->mm->context.flags);
| ^
include/linux/bitops.h:48:24: note: in definition of macro 'bitop'
48 | const##op(nr, addr) : op(nr, addr))
| ^~~~
arch/x86/kernel/process.c:1040:17: note: in expansion of macro 'test_bit'
1040 | return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
| ^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:46: error: 'mm_context_t' has no member named 'flags'
1041 | ¤t->mm->context.flags);
| ^
include/linux/bitops.h:48:39: note: in definition of macro 'bitop'
48 | const##op(nr, addr) : op(nr, addr))
| ^~~~
arch/x86/kernel/process.c:1040:17: note: in expansion of macro 'test_bit'
1040 | return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
| ^~~~~~~~
arch/x86/kernel/process.c: In function 'set_coco_user_hcall_mode':
arch/x86/kernel/process.c:1048:46: error: 'mm_context_t' has no member named 'flags'
1048 | ¤t->mm->context.flags);
| ^
arch/x86/kernel/process.c:1051:48: error: 'mm_context_t' has no member named 'flags'
1051 | ¤t->mm->context.flags);
| ^
arch/x86/kernel/process.c: In function 'get_coco_user_hcall_mode':
quoted
arch/x86/kernel/process.c:1042:1: warning: control reaches end of non-void function [-Wreturn-type]
1042 | }
| ^
vim +1041 arch/x86/kernel/process.c
1037
1038 static int get_coco_user_hcall_mode(void)
1039 {
1040 return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
arch/x86/kernel/process.c:1041:25: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
include/linux/bitops.h:45:37: note: in definition of macro 'bitop'
__builtin_constant_p((uintptr_t)(addr) != (uintptr_t)NULL) && \
^~~~
arch/x86/kernel/process.c:1040:10: note: in expansion of macro 'test_bit'
return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:25: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
include/linux/bitops.h:46:16: note: in definition of macro 'bitop'
(uintptr_t)(addr) != (uintptr_t)NULL && \
^~~~
arch/x86/kernel/process.c:1040:10: note: in expansion of macro 'test_bit'
return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:25: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
include/linux/bitops.h:47:50: note: in definition of macro 'bitop'
__builtin_constant_p(*(const unsigned long *)(addr))) ? \
^~~~
arch/x86/kernel/process.c:1040:10: note: in expansion of macro 'test_bit'
return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:25: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
include/linux/bitops.h:48:17: note: in definition of macro 'bitop'
const##op(nr, addr) : op(nr, addr))
^~~~
arch/x86/kernel/process.c:1040:10: note: in expansion of macro 'test_bit'
return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
^~~~~~~~
quoted
arch/x86/kernel/process.c:1041:25: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
include/linux/bitops.h:48:32: note: in definition of macro 'bitop'
const##op(nr, addr) : op(nr, addr))
^~~~
arch/x86/kernel/process.c:1040:10: note: in expansion of macro 'test_bit'
return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
^~~~~~~~
arch/x86/kernel/process.c: In function 'set_coco_user_hcall_mode':
arch/x86/kernel/process.c:1048:25: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
arch/x86/kernel/process.c:1051:27: error: 'mm_context_t' {aka 'struct <anonymous>'} has no member named 'flags'
¤t->mm->context.flags);
^
arch/x86/kernel/process.c: In function 'get_coco_user_hcall_mode':
arch/x86/kernel/process.c:1042:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
vim +1041 arch/x86/kernel/process.c
1037
1038 static int get_coco_user_hcall_mode(void)
1039 {
1040 return !test_bit(MM_CONTEXT_COCO_USER_HCALL,
From: Kirill A . Shutemov <hidden> Date: 2024-08-02 12:47:24
On Fri, Jul 26, 2024 at 06:58:00PM +0000, Tim Merrifield wrote:
Add a new process-level prctl option to enable/disable user-level
hypercalls when running in a confidential VM. Add support for
checking this flag on VMCALL #VE for TDX and transfer control to
a hypervisor vendor-specific handler.
From: Tim Merrifield <hidden> Date: 2024-07-26 18:49:27
This change adds a handler for tdx_hcall in the x86_hyper_runtime type for
VMware hypervisors which will ultimately invoke __tdx_hypercall. The
handler (vmware_tdx_user_hcall) does not reuse the existing
vmware_tdx_hypercall for a couple of reasons.
First, while the few hypercalls that are invoked from the kernel expect
uint32 outputs, this may not be the case for every backdoor userspace may
call. So the existing interface is not sufficient. Additionally, we don't
require the branches based on output arguments. Finally, the
VMWARE_CMD_MASK employed in vmware_tdx_hypercall is applicable to only
hypercalls expected from the kernel.
Signed-off-by: Tim Merrifield <redacted>
---
arch/x86/kernel/cpu/vmware.c | 51 ++++++++++++++++++++++++++++--------
1 file changed, 40 insertions(+), 11 deletions(-)
@@ -512,17 +530,7 @@ unsigned long vmware_tdx_hypercall(unsigned long cmd,returnULONG_MAX;}-args.rbx=in1;-args.rdx=in3;-args.rsi=in4;-args.rdi=in5;-args.r10=VMWARE_TDX_VENDOR_LEAF;-args.r11=VMWARE_TDX_HCALL_FUNC;-args.r12=VMWARE_HYPERVISOR_MAGIC;-args.r13=cmd;-/* CPL */-args.r15=0;-+vmware_init_tdx_args(&args,false,cmd,in1,in3,in4,in5,0);__tdx_hypercall(&args);if(out1)
@@ -539,6 +547,24 @@ unsigned long vmware_tdx_hypercall(unsigned long cmd,returnargs.r12;}EXPORT_SYMBOL_GPL(vmware_tdx_hypercall);++staticintvmware_tdx_user_hcall(structpt_regs*regs)+{+structtdx_module_argsargs={};++vmware_init_tdx_args(&args,true,regs->cx,regs->bx,+regs->dx,regs->si,regs->di,regs->bp);+__tdx_hypercall(&args);+regs->ax=args.r12;+regs->bx=args.rbx;+regs->cx=args.r13;+regs->dx=args.rdx;+regs->si=args.rsi;+regs->di=args.rdi;+regs->bp=args.r14;++return0;+}#endif#ifdef CONFIG_AMD_MEM_ENCRYPT