x86/shadow: tolerate failure of sh_set_toplevel_shadow()
authorJan Beulich <jbeulich@suse.com>
Tue, 11 Oct 2022 12:22:24 +0000 (14:22 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 11 Oct 2022 12:22:24 +0000 (14:22 +0200)
Subsequently sh_set_toplevel_shadow() will be adjusted to install a
blank entry in case prealloc fails. There are, in fact, pre-existing
error paths which would put in place a blank entry. The 4- and 2-level
code in sh_update_cr3(), however, assume the top level entry to be
valid.

Hence bail from the function in the unlikely event that it's not. Note
that 3-level logic works differently: In particular a guest is free to
supply a PDPTR pointing at 4 non-present (or otherwise deemed invalid)
entries. The guest will crash, but we already cope with that.

Really mfn_valid() is likely wrong to use in sh_set_toplevel_shadow(),
and it should instead be !mfn_eq(gmfn, INVALID_MFN). Avoid such a change
in security context, but add a respective assertion.

This is part of CVE-2022-33746 / XSA-410.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Tim Deegan <tim@xen.org>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
xen/arch/x86/mm/shadow/common.c
xen/arch/x86/mm/shadow/multi.c

index 3e1e43a3892a802c4e6dcd3169559c1331bbc6e4..a1961291a22e3272927d7847f23accc8d54b206f 100644 (file)
@@ -2521,6 +2521,7 @@ void sh_set_toplevel_shadow(struct vcpu *v,
     /* Now figure out the new contents: is this a valid guest MFN? */
     if ( !mfn_valid(gmfn) )
     {
+        ASSERT(mfn_eq(gmfn, INVALID_MFN));
         new_entry = pagetable_null();
         goto install_new_entry;
     }
index e10de449f184dc760727840e40c6d00c8dbdbf48..a51ec5d4f5d79ab6c903aaaa70be3eb1a68fc56a 100644 (file)
@@ -3316,6 +3316,11 @@ static void cf_check sh_update_cr3(struct vcpu *v, int do_locking, bool noflush)
     if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 )
         guest_flush_tlb_mask(d, d->dirty_cpumask);
     sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow);
+    if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) )
+    {
+        ASSERT(d->is_dying || d->is_shutting_down);
+        return;
+    }
     if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) )
     {
         mfn_t smfn = pagetable_get_mfn(v->arch.paging.shadow.shadow_table[0]);
@@ -3372,6 +3377,11 @@ static void cf_check sh_update_cr3(struct vcpu *v, int do_locking, bool noflush)
     if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 )
         guest_flush_tlb_mask(d, d->dirty_cpumask);
     sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow);
+    if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) )
+    {
+        ASSERT(d->is_dying || d->is_shutting_down);
+        return;
+    }
 #else
 #error This should never happen
 #endif