summaryrefslogtreecommitdiffstats
path: root/MdeModulePkg
diff options
context:
space:
mode:
authorZhiguang Liu <zhiguang.liu@intel.com>2024-03-11 15:32:19 +0800
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2024-04-16 04:41:27 +0000
commit74f6ce67343f81a842e6af3c599e4ae2a07ee51b (patch)
tree264c6c06f34ba315d98952350acb178512fe764c /MdeModulePkg
parentda7858117f12846e5c35922e8917fd0da07dcfef (diff)
downloadedk2-74f6ce67343f81a842e6af3c599e4ae2a07ee51b.tar.gz
edk2-74f6ce67343f81a842e6af3c599e4ae2a07ee51b.tar.bz2
edk2-74f6ce67343f81a842e6af3c599e4ae2a07ee51b.zip
MdeModulePkg/SMM: Support to unregister SMI handler in SMI handlers
This patch fix a use-after-free issue where unregistering an SMI handler could lead to the deletion of the SMI_HANDLER while it is still in use by SmiManage(). The fix involves modifying SmiHandlerUnRegister() to detect whether it is being called from within the SmiManage() stack. If so, the removal of the SMI_HANDLER is deferred until SmiManage() has finished executing. Additionally, due to the possibility of recursive SmiManage() calls, the unregistration and subsequent removal of the SMI_HANDLER are ensured to occur only after the outermost SmiManage() invocation has completed. Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Diffstat (limited to 'MdeModulePkg')
-rw-r--r--MdeModulePkg/Core/PiSmmCore/PiSmmCore.h1
-rw-r--r--MdeModulePkg/Core/PiSmmCore/Smi.c164
2 files changed, 139 insertions, 26 deletions
diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h
index b8a490a8c3..60073c78b4 100644
--- a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h
+++ b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h
@@ -93,6 +93,7 @@ typedef struct {
SMI_ENTRY *SmiEntry;
VOID *Context; // for profile
UINTN ContextSize; // for profile
+ BOOLEAN ToRemove; // To remove this SMI_HANDLER later
} SMI_HANDLER;
//
diff --git a/MdeModulePkg/Core/PiSmmCore/Smi.c b/MdeModulePkg/Core/PiSmmCore/Smi.c
index 2985f989c3..a84a1f48d3 100644
--- a/MdeModulePkg/Core/PiSmmCore/Smi.c
+++ b/MdeModulePkg/Core/PiSmmCore/Smi.c
@@ -8,6 +8,11 @@
#include "PiSmmCore.h"
+//
+// mSmiManageCallingDepth is used to track the depth of recursive calls of SmiManage.
+//
+UINTN mSmiManageCallingDepth = 0;
+
LIST_ENTRY mSmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList);
SMI_ENTRY mRootSmiEntry = {
@@ -80,6 +85,40 @@ SmmCoreFindSmiEntry (
}
/**
+ Remove SmiHandler and free the memory it used.
+ If SmiEntry is empty, remove SmiEntry and free the memory it used.
+
+ @param SmiHandler Points to SMI handler.
+ @param SmiEntry Points to SMI Entry or NULL for root SMI handlers.
+
+ @retval TRUE SmiEntry is removed.
+ @retval FALSE SmiEntry is not removed.
+**/
+BOOLEAN
+RemoveSmiHandler (
+ IN SMI_HANDLER *SmiHandler,
+ IN SMI_ENTRY *SmiEntry
+ )
+{
+ ASSERT (SmiHandler->ToRemove);
+ RemoveEntryList (&SmiHandler->Link);
+ FreePool (SmiHandler);
+
+ //
+ // Remove the SMI_ENTRY if all handlers have been removed.
+ //
+ if (SmiEntry != NULL) {
+ if (IsListEmpty (&SmiEntry->SmiHandlers)) {
+ RemoveEntryList (&SmiEntry->AllEntries);
+ FreePool (SmiEntry);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
Manage SMI of a particular type.
@param HandlerType Points to the handler type or NULL for root SMI handlers.
@@ -104,15 +143,17 @@ SmiManage (
{
LIST_ENTRY *Link;
LIST_ENTRY *Head;
+ LIST_ENTRY *EntryLink;
SMI_ENTRY *SmiEntry;
SMI_HANDLER *SmiHandler;
- BOOLEAN SuccessReturn;
+ EFI_STATUS ReturnStatus;
+ BOOLEAN WillReturn;
EFI_STATUS Status;
PERF_FUNCTION_BEGIN ();
-
- Status = EFI_NOT_FOUND;
- SuccessReturn = FALSE;
+ mSmiManageCallingDepth++;
+ Status = EFI_NOT_FOUND;
+ ReturnStatus = Status;
if (HandlerType == NULL) {
//
// Root SMI handler
@@ -152,7 +193,16 @@ SmiManage (
//
if (HandlerType != NULL) {
PERF_FUNCTION_END ();
- return EFI_INTERRUPT_PENDING;
+ ReturnStatus = EFI_INTERRUPT_PENDING;
+ WillReturn = TRUE;
+ } else {
+ //
+ // If any other handler's result sets ReturnStatus as EFI_SUCCESS, the return status
+ // will be EFI_SUCCESS.
+ //
+ if (ReturnStatus != EFI_SUCCESS) {
+ ReturnStatus = Status;
+ }
}
break;
@@ -165,10 +215,10 @@ SmiManage (
//
if (HandlerType != NULL) {
PERF_FUNCTION_END ();
- return EFI_SUCCESS;
+ WillReturn = TRUE;
}
- SuccessReturn = TRUE;
+ ReturnStatus = EFI_SUCCESS;
break;
case EFI_WARN_INTERRUPT_SOURCE_QUIESCED:
@@ -176,7 +226,7 @@ SmiManage (
// If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED
// then the function will return EFI_SUCCESS.
//
- SuccessReturn = TRUE;
+ ReturnStatus = EFI_SUCCESS;
break;
case EFI_WARN_INTERRUPT_SOURCE_PENDING:
@@ -184,6 +234,10 @@ SmiManage (
// If all the handlers returned EFI_WARN_INTERRUPT_SOURCE_PENDING
// then EFI_WARN_INTERRUPT_SOURCE_PENDING will be returned.
//
+ if (ReturnStatus != EFI_SUCCESS) {
+ ReturnStatus = Status;
+ }
+
break;
default:
@@ -193,14 +247,74 @@ SmiManage (
ASSERT (FALSE);
break;
}
+
+ if (WillReturn) {
+ break;
+ }
}
- if (SuccessReturn) {
- Status = EFI_SUCCESS;
+ ASSERT (mSmiManageCallingDepth > 0);
+ mSmiManageCallingDepth--;
+
+ //
+ // SmiHandlerUnRegister() calls from SMI handlers are deferred till this point.
+ // Before returned from SmiManage, delete the SmiHandler which is
+ // marked as ToRemove.
+ // Note that SmiManage can be called recursively.
+ //
+ if (mSmiManageCallingDepth == 0) {
+ //
+ // Go through all SmiHandler in root SMI handlers
+ //
+ for ( Link = GetFirstNode (&mRootSmiEntry.SmiHandlers)
+ ; !IsNull (&mRootSmiEntry.SmiHandlers, Link);
+ )
+ {
+ //
+ // SmiHandler might be removed in below, so cache the next link in Link
+ //
+ SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);
+ Link = GetNextNode (&mRootSmiEntry.SmiHandlers, Link);
+ if (SmiHandler->ToRemove) {
+ //
+ // Remove SmiHandler if the ToRemove is set.
+ //
+ RemoveSmiHandler (SmiHandler, NULL);
+ }
+ }
+
+ //
+ // Go through all SmiHandler in non-root SMI handlers
+ //
+ for ( EntryLink = GetFirstNode (&mSmiEntryList)
+ ; !IsNull (&mSmiEntryList, EntryLink);
+ )
+ {
+ //
+ // SmiEntry might be removed in below, so cache the next link in EntryLink
+ //
+ SmiEntry = CR (EntryLink, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE);
+ EntryLink = GetNextNode (&mSmiEntryList, EntryLink);
+ for ( Link = GetFirstNode (&SmiEntry->SmiHandlers)
+ ; !IsNull (&SmiEntry->SmiHandlers, Link);
+ )
+ {
+ //
+ // SmiHandler might be removed in below, so cache the next link in Link
+ //
+ SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);
+ Link = GetNextNode (&SmiEntry->SmiHandlers, Link);
+ if (SmiHandler->ToRemove) {
+ if (RemoveSmiHandler (SmiHandler, SmiEntry)) {
+ break;
+ }
+ }
+ }
+ }
}
PERF_FUNCTION_END ();
- return Status;
+ return ReturnStatus;
}
/**
@@ -238,6 +352,7 @@ SmiHandlerRegister (
SmiHandler->Signature = SMI_HANDLER_SIGNATURE;
SmiHandler->Handler = Handler;
SmiHandler->CallerAddr = (UINTN)RETURN_ADDRESS (0);
+ SmiHandler->ToRemove = FALSE;
if (HandlerType == NULL) {
//
@@ -318,30 +433,27 @@ SmiHandlerUnRegister (
}
}
- if ((EFI_HANDLE)SmiHandler != DispatchHandle) {
+ if (SmiHandler == NULL) {
return EFI_INVALID_PARAMETER;
}
- SmiEntry = SmiHandler->SmiEntry;
-
- RemoveEntryList (&SmiHandler->Link);
- FreePool (SmiHandler);
+ ASSERT ((EFI_HANDLE)SmiHandler == DispatchHandle);
+ SmiHandler->ToRemove = TRUE;
- if (SmiEntry == NULL) {
+ if (mSmiManageCallingDepth > 0) {
//
- // This is root SMI handler
+ // This function is called from SmiManage()
+ // Do not delete or remove SmiHandler or SmiEntry now.
+ // SmiManage will handle it later
//
return EFI_SUCCESS;
}
- if (IsListEmpty (&SmiEntry->SmiHandlers)) {
- //
- // No handler registered for this interrupt now, remove the SMI_ENTRY
- //
- RemoveEntryList (&SmiEntry->AllEntries);
-
- FreePool (SmiEntry);
- }
+ SmiEntry = SmiHandler->SmiEntry;
+ //
+ // For root SMI handler, use NULL for SmiEntry
+ //
+ RemoveSmiHandler (SmiHandler, (SmiEntry == &mRootSmiEntry) ? NULL : SmiEntry);
return EFI_SUCCESS;
}