Skip to content

Commit 589f2e4

Browse files
jiaxinwumergify[bot]
authored andcommitted
UefiCpuPkg/PiSmmCpuDxeSmm: Fix CP Exception when CET enable
Root cause: 1. Before DisableReadonlyPageWriteProtect() is called, the return address (coconut-svsm#1) is pushed in shadow stack. 2. CET is disabled. 3. DisableReadonlyPageWriteProtect() returns to coconut-svsm#1. 4. Page table is modified. 5. EnableReadonlyPageWriteProtect() is called, but the return address (coconut-svsm#2) is not pushed in shadow stack. 6. CET is enabled. 7. EnableReadonlyPageWriteProtect() returns to coconut-svsm#2. #CP exception happens because the actual return address (coconut-svsm#2) doesn't match the return address stored in shadow stack (coconut-svsm#1). Analysis: Shadow stack will stop update after CET disable (DisableCet() in DisableReadOnlyPageWriteProtect), but normal smi stack will be continue updated with the function called and return (DisableReadOnlyPageWriteProtect & EnableReadOnlyPageWriteProtect), thus leading stack mismatch after CET re-enabled (EnableCet() in EnableReadOnlyPageWriteProtect). According SDM Vol 3, 6.15-Control Protection Exception: Normal smi stack and shadow stack must be matched when CET enable, otherwise CP Exception will happen, which is caused by a near RET instruction. CET is disabled in DisableCet(), while can be enabled in EnableCet(). This way won't cause the problem because they are implemented in a way that return address of DisableCet() is poped out from shadow stack (Incsspq performs a pop to increases the shadow stack) and EnableCet() doesn't use "RET" but "JMP" to return to caller. So calling EnableCet() and DisableCet() doesn't have the same issue as calling DisableReadonlyPageWriteProtect() and EnableReadonlyPageWriteProtect(). With above root cause & analysis, define below 2 macros instead of functions for WP & CET operation: WRITE_UNPROTECT_RO_PAGES (Wp, Cet) WRITE_PROTECT_RO_PAGES (Wp, Cet) Because DisableCet() & EnableCet() must be in the same function to avoid shadow stack and normal SMI stack mismatch. Note: WRITE_UNPROTECT_RO_PAGES () must be called pair with WRITE_PROTECT_RO_PAGES () in same function. Change-Id: I4e126697efcd8dbfb4887da034d8691bfca969e3 Cc: Eric Dong <[email protected]> Cc: Ray Ni <[email protected]> Cc: Zeng Star <[email protected]> Cc: Gerd Hoffmann <[email protected]> Cc: Rahul Kumar <[email protected]> Cc: Laszlo Ersek <[email protected]> Signed-off-by: Jiaxin Wu <[email protected]> Reviewed-by: Laszlo Ersek <[email protected]> Reviewed-by: Ray Ni <[email protected]> Reviewed-by: Eric Dong <[email protected]>
1 parent 35c0c63 commit 589f2e4

File tree

3 files changed

+81
-58
lines changed

3 files changed

+81
-58
lines changed

UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h

+47-12
Original file line numberDiff line numberDiff line change
@@ -1553,27 +1553,62 @@ SmmWaitForApArrival (
15531553
);
15541554

15551555
/**
1556-
Disable Write Protect on pages marked as read-only if Cr0.Bits.WP is 1.
1556+
Write unprotect read-only pages if Cr0.Bits.WP is 1.
1557+
1558+
@param[out] WriteProtect If Cr0.Bits.WP is enabled.
15571559
1558-
@param[out] WpEnabled If Cr0.WP is enabled.
1559-
@param[out] CetEnabled If CET is enabled.
15601560
**/
15611561
VOID
1562-
DisableReadOnlyPageWriteProtect (
1563-
OUT BOOLEAN *WpEnabled,
1564-
OUT BOOLEAN *CetEnabled
1562+
SmmWriteUnprotectReadOnlyPage (
1563+
OUT BOOLEAN *WriteProtect
15651564
);
15661565

15671566
/**
1568-
Enable Write Protect on pages marked as read-only.
1567+
Write protect read-only pages.
1568+
1569+
@param[in] WriteProtect If Cr0.Bits.WP should be enabled.
15691570
1570-
@param[out] WpEnabled If Cr0.WP should be enabled.
1571-
@param[out] CetEnabled If CET should be enabled.
15721571
**/
15731572
VOID
1574-
EnableReadOnlyPageWriteProtect (
1575-
BOOLEAN WpEnabled,
1576-
BOOLEAN CetEnabled
1573+
SmmWriteProtectReadOnlyPage (
1574+
IN BOOLEAN WriteProtect
15771575
);
15781576

1577+
///
1578+
/// Define macros to encapsulate the write unprotect/protect
1579+
/// read-only pages.
1580+
/// Below pieces of logic are defined as macros and not functions
1581+
/// because "CET" feature disable & enable must be in the same
1582+
/// function to avoid shadow stack and normal SMI stack mismatch,
1583+
/// thus WRITE_UNPROTECT_RO_PAGES () must be called pair with
1584+
/// WRITE_PROTECT_RO_PAGES () in same function.
1585+
///
1586+
/// @param[in,out] Wp A BOOLEAN variable local to the containing
1587+
/// function, carrying write protection status from
1588+
/// WRITE_UNPROTECT_RO_PAGES() to
1589+
/// WRITE_PROTECT_RO_PAGES().
1590+
///
1591+
/// @param[in,out] Cet A BOOLEAN variable local to the containing
1592+
/// function, carrying control flow integrity
1593+
/// enforcement status from
1594+
/// WRITE_UNPROTECT_RO_PAGES() to
1595+
/// WRITE_PROTECT_RO_PAGES().
1596+
///
1597+
#define WRITE_UNPROTECT_RO_PAGES(Wp, Cet) \
1598+
do { \
1599+
Cet = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0); \
1600+
if (Cet) { \
1601+
DisableCet (); \
1602+
} \
1603+
SmmWriteUnprotectReadOnlyPage (&Wp); \
1604+
} while (FALSE)
1605+
1606+
#define WRITE_PROTECT_RO_PAGES(Wp, Cet) \
1607+
do { \
1608+
SmmWriteProtectReadOnlyPage (Wp); \
1609+
if (Cet) { \
1610+
EnableCet (); \
1611+
} \
1612+
} while (FALSE)
1613+
15791614
#endif

UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c

+30-43
Original file line numberDiff line numberDiff line change
@@ -41,60 +41,43 @@ PAGE_TABLE_POOL *mPageTablePool = NULL;
4141
BOOLEAN mIsReadOnlyPageTable = FALSE;
4242

4343
/**
44-
Disable Write Protect on pages marked as read-only if Cr0.Bits.WP is 1.
44+
Write unprotect read-only pages if Cr0.Bits.WP is 1.
45+
46+
@param[out] WriteProtect If Cr0.Bits.WP is enabled.
4547
46-
@param[out] WpEnabled If Cr0.WP is enabled.
47-
@param[out] CetEnabled If CET is enabled.
4848
**/
4949
VOID
50-
DisableReadOnlyPageWriteProtect (
51-
OUT BOOLEAN *WpEnabled,
52-
OUT BOOLEAN *CetEnabled
50+
SmmWriteUnprotectReadOnlyPage (
51+
OUT BOOLEAN *WriteProtect
5352
)
5453
{
5554
IA32_CR0 Cr0;
5655

57-
*CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
58-
Cr0.UintN = AsmReadCr0 ();
59-
*WpEnabled = (Cr0.Bits.WP != 0) ? TRUE : FALSE;
60-
if (*WpEnabled) {
61-
if (*CetEnabled) {
62-
//
63-
// CET must be disabled if WP is disabled. Disable CET before clearing CR0.WP.
64-
//
65-
DisableCet ();
66-
}
67-
56+
Cr0.UintN = AsmReadCr0 ();
57+
*WriteProtect = (Cr0.Bits.WP != 0);
58+
if (*WriteProtect) {
6859
Cr0.Bits.WP = 0;
6960
AsmWriteCr0 (Cr0.UintN);
7061
}
7162
}
7263

7364
/**
74-
Enable Write Protect on pages marked as read-only.
65+
Write protect read-only pages.
66+
67+
@param[in] WriteProtect If Cr0.Bits.WP should be enabled.
7568
76-
@param[out] WpEnabled If Cr0.WP should be enabled.
77-
@param[out] CetEnabled If CET should be enabled.
7869
**/
7970
VOID
80-
EnableReadOnlyPageWriteProtect (
81-
BOOLEAN WpEnabled,
82-
BOOLEAN CetEnabled
71+
SmmWriteProtectReadOnlyPage (
72+
IN BOOLEAN WriteProtect
8373
)
8474
{
8575
IA32_CR0 Cr0;
8676

87-
if (WpEnabled) {
77+
if (WriteProtect) {
8878
Cr0.UintN = AsmReadCr0 ();
8979
Cr0.Bits.WP = 1;
9080
AsmWriteCr0 (Cr0.UintN);
91-
92-
if (CetEnabled) {
93-
//
94-
// re-enable CET.
95-
//
96-
EnableCet ();
97-
}
9881
}
9982
}
10083

@@ -121,7 +104,7 @@ InitializePageTablePool (
121104
)
122105
{
123106
VOID *Buffer;
124-
BOOLEAN WpEnabled;
107+
BOOLEAN WriteProtect;
125108
BOOLEAN CetEnabled;
126109

127110
//
@@ -159,9 +142,11 @@ InitializePageTablePool (
159142
// If page table memory has been marked as RO, mark the new pool pages as read-only.
160143
//
161144
if (mIsReadOnlyPageTable) {
162-
DisableReadOnlyPageWriteProtect (&WpEnabled, &CetEnabled);
145+
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
146+
163147
SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);
164-
EnableReadOnlyPageWriteProtect (WpEnabled, CetEnabled);
148+
149+
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
165150
}
166151

167152
return TRUE;
@@ -1011,7 +996,7 @@ SetMemMapAttributes (
1011996
IA32_MAP_ENTRY *Map;
1012997
UINTN Count;
1013998
UINT64 MemoryAttribute;
1014-
BOOLEAN WpEnabled;
999+
BOOLEAN WriteProtect;
10151000
BOOLEAN CetEnabled;
10161001

10171002
SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
@@ -1057,7 +1042,7 @@ SetMemMapAttributes (
10571042

10581043
ASSERT_RETURN_ERROR (Status);
10591044

1060-
DisableReadOnlyPageWriteProtect (&WpEnabled, &CetEnabled);
1045+
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
10611046

10621047
MemoryMap = MemoryMapStart;
10631048
for (Index = 0; Index < MemoryMapEntryCount; Index++) {
@@ -1087,7 +1072,8 @@ SetMemMapAttributes (
10871072
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
10881073
}
10891074

1090-
EnableReadOnlyPageWriteProtect (WpEnabled, CetEnabled);
1075+
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
1076+
10911077
FreePool (Map);
10921078

10931079
PatchSmmSaveStateMap ();
@@ -1394,14 +1380,14 @@ SetUefiMemMapAttributes (
13941380
UINTN MemoryMapEntryCount;
13951381
UINTN Index;
13961382
EFI_MEMORY_DESCRIPTOR *Entry;
1397-
BOOLEAN WpEnabled;
1383+
BOOLEAN WriteProtect;
13981384
BOOLEAN CetEnabled;
13991385

14001386
PERF_FUNCTION_BEGIN ();
14011387

14021388
DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
14031389

1404-
DisableReadOnlyPageWriteProtect (&WpEnabled, &CetEnabled);
1390+
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
14051391

14061392
if (mUefiMemoryMap != NULL) {
14071393
MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
@@ -1481,7 +1467,7 @@ SetUefiMemMapAttributes (
14811467
}
14821468
}
14831469

1484-
EnableReadOnlyPageWriteProtect (WpEnabled, CetEnabled);
1470+
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
14851471

14861472
//
14871473
// Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
@@ -1872,7 +1858,7 @@ SetPageTableAttributes (
18721858
VOID
18731859
)
18741860
{
1875-
BOOLEAN WpEnabled;
1861+
BOOLEAN WriteProtect;
18761862
BOOLEAN CetEnabled;
18771863

18781864
if (!IfReadOnlyPageTableNeeded ()) {
@@ -1886,7 +1872,7 @@ SetPageTableAttributes (
18861872
// Disable write protection, because we need mark page table to be write protected.
18871873
// We need *write* page table memory, to mark itself to be *read only*.
18881874
//
1889-
DisableReadOnlyPageWriteProtect (&WpEnabled, &CetEnabled);
1875+
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
18901876

18911877
// Set memory used by page table as Read Only.
18921878
DEBUG ((DEBUG_INFO, "Start...\n"));
@@ -1895,7 +1881,8 @@ SetPageTableAttributes (
18951881
//
18961882
// Enable write protection, after page table attribute updated.
18971883
//
1898-
EnableReadOnlyPageWriteProtect (TRUE, CetEnabled);
1884+
WRITE_PROTECT_RO_PAGES (TRUE, CetEnabled);
1885+
18991886
mIsReadOnlyPageTable = TRUE;
19001887

19011888
//

UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ InitPaging (
594594
UINT64 Limit;
595595
UINT64 PreviousAddress;
596596
UINT64 MemoryAttrMask;
597-
BOOLEAN WpEnabled;
597+
BOOLEAN WriteProtect;
598598
BOOLEAN CetEnabled;
599599

600600
PERF_FUNCTION_BEGIN ();
@@ -606,7 +606,8 @@ InitPaging (
606606
Limit = (IsRestrictedMemoryAccess ()) ? LShiftU64 (1, mPhysicalAddressBits) : BASE_4GB;
607607
}
608608

609-
DisableReadOnlyPageWriteProtect (&WpEnabled, &CetEnabled);
609+
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
610+
610611
//
611612
// [0, 4k] may be non-present.
612613
//
@@ -672,7 +673,7 @@ InitPaging (
672673
ASSERT_RETURN_ERROR (Status);
673674
}
674675

675-
EnableReadOnlyPageWriteProtect (WpEnabled, CetEnabled);
676+
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
676677

677678
//
678679
// Flush TLB

0 commit comments

Comments
 (0)