Skip to content

Commit ad5b353

Browse files
tlendackybonzini
authored andcommitted
KVM: SVM: Do not terminate SEV-ES guests on GHCB validation failure
Currently, an SEV-ES guest is terminated if the validation of the VMGEXIT exit code or exit parameters fails. The VMGEXIT instruction can be issued from userspace, even though userspace (likely) can't update the GHCB. To prevent userspace from being able to kill the guest, return an error through the GHCB when validation fails rather than terminating the guest. For cases where the GHCB can't be updated (e.g. the GHCB can't be mapped, etc.), just return back to the guest. The new error codes are documented in the lasest update to the GHCB specification. Fixes: 291bd20 ("KVM: SVM: Add initial support for a VMGEXIT VMEXIT") Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Message-Id: <b57280b5562893e2616257ac9c2d4525a9aeeb42.1638471124.git.thomas.lendacky@amd.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent a655276 commit ad5b353

File tree

2 files changed

+71
-46
lines changed

2 files changed

+71
-46
lines changed

arch/x86/include/asm/sev-common.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,15 @@
7373

7474
#define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)
7575

76+
/*
77+
* Error codes related to GHCB input that can be communicated back to the guest
78+
* by setting the lower 32-bits of the GHCB SW_EXITINFO1 field to 2.
79+
*/
80+
#define GHCB_ERR_NOT_REGISTERED 1
81+
#define GHCB_ERR_INVALID_USAGE 2
82+
#define GHCB_ERR_INVALID_SCRATCH_AREA 3
83+
#define GHCB_ERR_MISSING_INPUT 4
84+
#define GHCB_ERR_INVALID_INPUT 5
85+
#define GHCB_ERR_INVALID_EVENT 6
86+
7687
#endif

arch/x86/kvm/svm/sev.c

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,24 +2352,29 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm)
23522352
memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap));
23532353
}
23542354

2355-
static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
2355+
static bool sev_es_validate_vmgexit(struct vcpu_svm *svm)
23562356
{
23572357
struct kvm_vcpu *vcpu;
23582358
struct ghcb *ghcb;
2359-
u64 exit_code = 0;
2359+
u64 exit_code;
2360+
u64 reason;
23602361

23612362
ghcb = svm->sev_es.ghcb;
23622363

2363-
/* Only GHCB Usage code 0 is supported */
2364-
if (ghcb->ghcb_usage)
2365-
goto vmgexit_err;
2366-
23672364
/*
2368-
* Retrieve the exit code now even though is may not be marked valid
2365+
* Retrieve the exit code now even though it may not be marked valid
23692366
* as it could help with debugging.
23702367
*/
23712368
exit_code = ghcb_get_sw_exit_code(ghcb);
23722369

2370+
/* Only GHCB Usage code 0 is supported */
2371+
if (ghcb->ghcb_usage) {
2372+
reason = GHCB_ERR_INVALID_USAGE;
2373+
goto vmgexit_err;
2374+
}
2375+
2376+
reason = GHCB_ERR_MISSING_INPUT;
2377+
23732378
if (!ghcb_sw_exit_code_is_valid(ghcb) ||
23742379
!ghcb_sw_exit_info_1_is_valid(ghcb) ||
23752380
!ghcb_sw_exit_info_2_is_valid(ghcb))
@@ -2448,30 +2453,34 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
24482453
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
24492454
break;
24502455
default:
2456+
reason = GHCB_ERR_INVALID_EVENT;
24512457
goto vmgexit_err;
24522458
}
24532459

2454-
return 0;
2460+
return true;
24552461

24562462
vmgexit_err:
24572463
vcpu = &svm->vcpu;
24582464

2459-
if (ghcb->ghcb_usage) {
2465+
if (reason == GHCB_ERR_INVALID_USAGE) {
24602466
vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n",
24612467
ghcb->ghcb_usage);
2468+
} else if (reason == GHCB_ERR_INVALID_EVENT) {
2469+
vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n",
2470+
exit_code);
24622471
} else {
2463-
vcpu_unimpl(vcpu, "vmgexit: exit reason %#llx is not valid\n",
2472+
vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n",
24642473
exit_code);
24652474
dump_ghcb(svm);
24662475
}
24672476

2468-
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
2469-
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON;
2470-
vcpu->run->internal.ndata = 2;
2471-
vcpu->run->internal.data[0] = exit_code;
2472-
vcpu->run->internal.data[1] = vcpu->arch.last_vmentry_cpu;
2477+
/* Clear the valid entries fields */
2478+
memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap));
2479+
2480+
ghcb_set_sw_exit_info_1(ghcb, 2);
2481+
ghcb_set_sw_exit_info_2(ghcb, reason);
24732482

2474-
return -EINVAL;
2483+
return false;
24752484
}
24762485

24772486
void sev_es_unmap_ghcb(struct vcpu_svm *svm)
@@ -2530,7 +2539,7 @@ void pre_sev_run(struct vcpu_svm *svm, int cpu)
25302539
}
25312540

25322541
#define GHCB_SCRATCH_AREA_LIMIT (16ULL * PAGE_SIZE)
2533-
static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
2542+
static bool setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
25342543
{
25352544
struct vmcb_control_area *control = &svm->vmcb->control;
25362545
struct ghcb *ghcb = svm->sev_es.ghcb;
@@ -2541,14 +2550,14 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
25412550
scratch_gpa_beg = ghcb_get_sw_scratch(ghcb);
25422551
if (!scratch_gpa_beg) {
25432552
pr_err("vmgexit: scratch gpa not provided\n");
2544-
return -EINVAL;
2553+
goto e_scratch;
25452554
}
25462555

25472556
scratch_gpa_end = scratch_gpa_beg + len;
25482557
if (scratch_gpa_end < scratch_gpa_beg) {
25492558
pr_err("vmgexit: scratch length (%#llx) not valid for scratch address (%#llx)\n",
25502559
len, scratch_gpa_beg);
2551-
return -EINVAL;
2560+
goto e_scratch;
25522561
}
25532562

25542563
if ((scratch_gpa_beg & PAGE_MASK) == control->ghcb_gpa) {
@@ -2566,7 +2575,7 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
25662575
scratch_gpa_end > ghcb_scratch_end) {
25672576
pr_err("vmgexit: scratch area is outside of GHCB shared buffer area (%#llx - %#llx)\n",
25682577
scratch_gpa_beg, scratch_gpa_end);
2569-
return -EINVAL;
2578+
goto e_scratch;
25702579
}
25712580

25722581
scratch_va = (void *)svm->sev_es.ghcb;
@@ -2579,18 +2588,18 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
25792588
if (len > GHCB_SCRATCH_AREA_LIMIT) {
25802589
pr_err("vmgexit: scratch area exceeds KVM limits (%#llx requested, %#llx limit)\n",
25812590
len, GHCB_SCRATCH_AREA_LIMIT);
2582-
return -EINVAL;
2591+
goto e_scratch;
25832592
}
25842593
scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT);
25852594
if (!scratch_va)
2586-
return -ENOMEM;
2595+
goto e_scratch;
25872596

25882597
if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, len)) {
25892598
/* Unable to copy scratch area from guest */
25902599
pr_err("vmgexit: kvm_read_guest for scratch area failed\n");
25912600

25922601
kvfree(scratch_va);
2593-
return -EFAULT;
2602+
goto e_scratch;
25942603
}
25952604

25962605
/*
@@ -2606,7 +2615,13 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
26062615
svm->sev_es.ghcb_sa = scratch_va;
26072616
svm->sev_es.ghcb_sa_len = len;
26082617

2609-
return 0;
2618+
return true;
2619+
2620+
e_scratch:
2621+
ghcb_set_sw_exit_info_1(ghcb, 2);
2622+
ghcb_set_sw_exit_info_2(ghcb, GHCB_ERR_INVALID_SCRATCH_AREA);
2623+
2624+
return false;
26102625
}
26112626

26122627
static void set_ghcb_msr_bits(struct vcpu_svm *svm, u64 value, u64 mask,
@@ -2657,7 +2672,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
26572672

26582673
ret = svm_invoke_exit_handler(vcpu, SVM_EXIT_CPUID);
26592674
if (!ret) {
2660-
ret = -EINVAL;
2675+
/* Error, keep GHCB MSR value as-is */
26612676
break;
26622677
}
26632678

@@ -2693,10 +2708,13 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
26932708
GHCB_MSR_TERM_REASON_POS);
26942709
pr_info("SEV-ES guest requested termination: %#llx:%#llx\n",
26952710
reason_set, reason_code);
2696-
fallthrough;
2711+
2712+
ret = -EINVAL;
2713+
break;
26972714
}
26982715
default:
2699-
ret = -EINVAL;
2716+
/* Error, keep GHCB MSR value as-is */
2717+
break;
27002718
}
27012719

27022720
trace_kvm_vmgexit_msr_protocol_exit(svm->vcpu.vcpu_id,
@@ -2720,14 +2738,18 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
27202738

27212739
if (!ghcb_gpa) {
27222740
vcpu_unimpl(vcpu, "vmgexit: GHCB gpa is not set\n");
2723-
return -EINVAL;
2741+
2742+
/* Without a GHCB, just return right back to the guest */
2743+
return 1;
27242744
}
27252745

27262746
if (kvm_vcpu_map(vcpu, ghcb_gpa >> PAGE_SHIFT, &svm->sev_es.ghcb_map)) {
27272747
/* Unable to map GHCB from guest */
27282748
vcpu_unimpl(vcpu, "vmgexit: error mapping GHCB [%#llx] from guest\n",
27292749
ghcb_gpa);
2730-
return -EINVAL;
2750+
2751+
/* Without a GHCB, just return right back to the guest */
2752+
return 1;
27312753
}
27322754

27332755
svm->sev_es.ghcb = svm->sev_es.ghcb_map.hva;
@@ -2737,18 +2759,17 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
27372759

27382760
exit_code = ghcb_get_sw_exit_code(ghcb);
27392761

2740-
ret = sev_es_validate_vmgexit(svm);
2741-
if (ret)
2742-
return ret;
2762+
if (!sev_es_validate_vmgexit(svm))
2763+
return 1;
27432764

27442765
sev_es_sync_from_ghcb(svm);
27452766
ghcb_set_sw_exit_info_1(ghcb, 0);
27462767
ghcb_set_sw_exit_info_2(ghcb, 0);
27472768

2769+
ret = 1;
27482770
switch (exit_code) {
27492771
case SVM_VMGEXIT_MMIO_READ:
2750-
ret = setup_vmgexit_scratch(svm, true, control->exit_info_2);
2751-
if (ret)
2772+
if (!setup_vmgexit_scratch(svm, true, control->exit_info_2))
27522773
break;
27532774

27542775
ret = kvm_sev_es_mmio_read(vcpu,
@@ -2757,8 +2778,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
27572778
svm->sev_es.ghcb_sa);
27582779
break;
27592780
case SVM_VMGEXIT_MMIO_WRITE:
2760-
ret = setup_vmgexit_scratch(svm, false, control->exit_info_2);
2761-
if (ret)
2781+
if (!setup_vmgexit_scratch(svm, false, control->exit_info_2))
27622782
break;
27632783

27642784
ret = kvm_sev_es_mmio_write(vcpu,
@@ -2787,14 +2807,10 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
27872807
default:
27882808
pr_err("svm: vmgexit: unsupported AP jump table request - exit_info_1=%#llx\n",
27892809
control->exit_info_1);
2790-
ghcb_set_sw_exit_info_1(ghcb, 1);
2791-
ghcb_set_sw_exit_info_2(ghcb,
2792-
X86_TRAP_UD |
2793-
SVM_EVTINJ_TYPE_EXEPT |
2794-
SVM_EVTINJ_VALID);
2810+
ghcb_set_sw_exit_info_1(ghcb, 2);
2811+
ghcb_set_sw_exit_info_2(ghcb, GHCB_ERR_INVALID_INPUT);
27952812
}
27962813

2797-
ret = 1;
27982814
break;
27992815
}
28002816
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
@@ -2814,7 +2830,6 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in)
28142830
{
28152831
int count;
28162832
int bytes;
2817-
int r;
28182833

28192834
if (svm->vmcb->control.exit_info_2 > INT_MAX)
28202835
return -EINVAL;
@@ -2823,9 +2838,8 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in)
28232838
if (unlikely(check_mul_overflow(count, size, &bytes)))
28242839
return -EINVAL;
28252840

2826-
r = setup_vmgexit_scratch(svm, in, bytes);
2827-
if (r)
2828-
return r;
2841+
if (!setup_vmgexit_scratch(svm, in, bytes))
2842+
return 1;
28292843

28302844
return kvm_sev_es_string_io(&svm->vcpu, size, port, svm->sev_es.ghcb_sa,
28312845
count, in);

0 commit comments

Comments
 (0)