Patch "x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad()" has been added to the 6.4-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad()

to the 6.4-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     x86-tdx-fix-race-between-set_memory_encrypted-and-lo.patch
and it can be found in the queue-6.4 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit b70de5d8c7558d22965dc021ca78348d6bd281cc
Author: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
Date:   Tue Jun 6 12:56:21 2023 +0300

    x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad()
    
    [ Upstream commit 195edce08b63d293377f615f4f7f086715d2d212 ]
    
    tl;dr: There is a race in the TDX private<=>shared conversion code
           which could kill the TDX guest.  Fix it by changing conversion
           ordering to eliminate the window.
    
    TDX hardware maintains metadata to track which pages are private and
    shared. Additionally, TDX guests use the guest x86 page tables to
    specify whether a given mapping is intended to be private or shared.
    Bad things happen when the intent and metadata do not match.
    
    So there are two thing in play:
     1. "the page" -- the physical TDX page metadata
     2. "the mapping" -- the guest-controlled x86 page table intent
    
    For instance, an unrecoverable exit to VMM occurs if a guest touches a
    private mapping that points to a shared physical page.
    
    In summary:
            * Private mapping => Private Page == OK (obviously)
            * Shared mapping  => Shared Page  == OK (obviously)
            * Private mapping => Shared Page  == BIG BOOM!
            * Shared mapping  => Private Page == OK-ish
              (It will read generate a recoverable #VE via handle_mmio())
    
    Enter load_unaligned_zeropad(). It can touch memory that is adjacent but
    otherwise unrelated to the memory it needs to touch. It will cause one
    of those unrecoverable exits (aka. BIG BOOM) if it blunders into a
    shared mapping pointing to a private page.
    
    This is a problem when __set_memory_enc_pgtable() converts pages from
    shared to private. It first changes the mapping and second modifies
    the TDX page metadata.  It's moving from:
    
            * Shared mapping  => Shared Page  == OK
    to:
            * Private mapping => Shared Page  == BIG BOOM!
    
    This means that there is a window with a shared mapping pointing to a
    private page where load_unaligned_zeropad() can strike.
    
    Add a TDX handler for guest.enc_status_change_prepare(). This converts
    the page from shared to private *before* the page becomes private.  This
    ensures that there is never a private mapping to a shared page.
    
    Leave a guest.enc_status_change_finish() in place but only use it for
    private=>shared conversions.  This will delay updating the TDX metadata
    marking the page private until *after* the mapping matches the metadata.
    This also ensures that there is never a private mapping to a shared page.
    
    [ dhansen: rewrite changelog ]
    
    Fixes: 7dbde7631629 ("x86/mm/cpa: Add support for TDX shared memory")
    Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
    Signed-off-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
    Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@xxxxxxxxxxxxxxx>
    Link: https://lore.kernel.org/all/20230606095622.1939-3-kirill.shutemov%40linux.intel.com
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index e146b599260f8..64f1343df062f 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -840,6 +840,30 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
 	return true;
 }
 
+static bool tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,
+					  bool enc)
+{
+	/*
+	 * Only handle shared->private conversion here.
+	 * See the comment in tdx_early_init().
+	 */
+	if (enc)
+		return tdx_enc_status_changed(vaddr, numpages, enc);
+	return true;
+}
+
+static bool tdx_enc_status_change_finish(unsigned long vaddr, int numpages,
+					 bool enc)
+{
+	/*
+	 * Only handle private->shared conversion here.
+	 * See the comment in tdx_early_init().
+	 */
+	if (!enc)
+		return tdx_enc_status_changed(vaddr, numpages, enc);
+	return true;
+}
+
 void __init tdx_early_init(void)
 {
 	u64 cc_mask;
@@ -867,9 +891,30 @@ void __init tdx_early_init(void)
 	 */
 	physical_mask &= cc_mask - 1;
 
-	x86_platform.guest.enc_cache_flush_required = tdx_cache_flush_required;
-	x86_platform.guest.enc_tlb_flush_required   = tdx_tlb_flush_required;
-	x86_platform.guest.enc_status_change_finish = tdx_enc_status_changed;
+	/*
+	 * The kernel mapping should match the TDX metadata for the page.
+	 * load_unaligned_zeropad() can touch memory *adjacent* to that which is
+	 * owned by the caller and can catch even _momentary_ mismatches.  Bad
+	 * things happen on mismatch:
+	 *
+	 *   - Private mapping => Shared Page  == Guest shutdown
+         *   - Shared mapping  => Private Page == Recoverable #VE
+	 *
+	 * guest.enc_status_change_prepare() converts the page from
+	 * shared=>private before the mapping becomes private.
+	 *
+	 * guest.enc_status_change_finish() converts the page from
+	 * private=>shared after the mapping becomes private.
+	 *
+	 * In both cases there is a temporary shared mapping to a private page,
+	 * which can result in a #VE.  But, there is never a private mapping to
+	 * a shared page.
+	 */
+	x86_platform.guest.enc_status_change_prepare = tdx_enc_status_change_prepare;
+	x86_platform.guest.enc_status_change_finish  = tdx_enc_status_change_finish;
+
+	x86_platform.guest.enc_cache_flush_required  = tdx_cache_flush_required;
+	x86_platform.guest.enc_tlb_flush_required    = tdx_tlb_flush_required;
 
 	pr_info("Guest detected\n");
 }



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux