]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/VariableAuthenticated/RuntimeDxe/VariableSmm.c
ShellPkg: Fix pointer initialization error of “ShellOpt” Shell environment variable
[mirror_edk2.git] / SecurityPkg / VariableAuthenticated / RuntimeDxe / VariableSmm.c
index 52d9aa041af4f554cf471b0ab2b65954819bea9b..316845f045d5d0494392cbe4caa735bb658aa88c 100644 (file)
@@ -3,7 +3,18 @@
   implements an SMI handler to communicate with the DXE runtime driver \r
   to provide variable services.\r
 \r
-Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>\r
+  Caution: This module requires additional review when modified.\r
+  This driver will have external input - variable data and communicate buffer in SMM mode.\r
+  This external input must be validated carefully to avoid security issue like\r
+  buffer overflow, integer overflow.\r
+\r
+  SmmVariableHandler() will receive untrusted input and do basic validation.\r
+\r
+  Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(), \r
+  VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), \r
+  SmmVariableGetStatistics() should also do validation based on its own knowledge.\r
+\r
+Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>\r
 This program and the accompanying materials \r
 are licensed and made available under the terms and conditions of the BSD License \r
 which accompanies this distribution.  The full text of the license may be found at \r
@@ -17,12 +28,17 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Protocol/SmmVariable.h>\r
 #include <Protocol/SmmFirmwareVolumeBlock.h>\r
 #include <Protocol/SmmFaultTolerantWrite.h>\r
+#include <Protocol/SmmAccess2.h>\r
+\r
 #include <Library/SmmServicesTableLib.h>\r
 \r
 #include <Guid/AuthenticatedVariableFormat.h>\r
 #include <Guid/SmmVariableCommon.h>\r
 #include "Variable.h"\r
 \r
+EFI_SMRAM_DESCRIPTOR                                 *mSmramRanges;\r
+UINTN                                                mSmramRangeCount;\r
+\r
 extern VARIABLE_INFO_ENTRY                           *gVariableInfo;\r
 EFI_HANDLE                                           mSmmVariableHandle      = NULL;\r
 EFI_HANDLE                                           mVariableHandle         = NULL;\r
@@ -50,6 +66,34 @@ AtRuntime (
   return mAtRuntime;\r
 }\r
 \r
+/**\r
+  This function check if the address is in SMRAM.\r
+\r
+  @param Buffer  the buffer address to be checked.\r
+  @param Length  the buffer length to be checked.\r
+\r
+  @retval TRUE  this address is in SMRAM.\r
+  @retval FALSE this address is NOT in SMRAM.\r
+**/\r
+BOOLEAN\r
+InternalIsAddressInSmram (\r
+  IN EFI_PHYSICAL_ADDRESS  Buffer,\r
+  IN UINT64                Length\r
+  )\r
+{\r
+  UINTN  Index;\r
+\r
+  for (Index = 0; Index < mSmramRangeCount; Index ++) {\r
+    if (((Buffer >= mSmramRanges[Index].CpuStart) && (Buffer < mSmramRanges[Index].CpuStart + mSmramRanges[Index].PhysicalSize)) ||\r
+        ((mSmramRanges[Index].CpuStart >= Buffer) && (mSmramRanges[Index].CpuStart < Buffer + Length))) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
 /**\r
   Initializes a basic mutual exclusion lock.\r
 \r
@@ -241,17 +285,21 @@ GetFvbCountAndBuffer (
 /**\r
   Get the variable statistics information from the information buffer pointed by gVariableInfo.\r
 \r
-  @param[in, out]  InfoEntry   A pointer to the buffer of variable information entry.\r
-                               On input, point to the variable information returned last time. if \r
-                               InfoEntry->VendorGuid is zero, return the first information.\r
-                               On output, point to the next variable information.\r
-  @param[in, out]  InfoSize    On input, the size of the variable information buffer.\r
-                               On output, the returned variable information size.\r
+  Caution: This function may be invoked at SMM runtime.\r
+  InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.\r
+\r
+  @param[in, out]  InfoEntry    A pointer to the buffer of variable information entry.\r
+                                On input, point to the variable information returned last time. if \r
+                                InfoEntry->VendorGuid is zero, return the first information.\r
+                                On output, point to the next variable information.\r
+  @param[in, out]  InfoSize     On input, the size of the variable information buffer.\r
+                                On output, the returned variable information size.\r
 \r
-  @retval EFI_SUCCESS          The variable information is found and returned successfully.\r
-  @retval EFI_UNSUPPORTED      No variable inoformation exists in variable driver. The \r
-                               PcdVariableCollectStatistics should be set TRUE to support it.\r
-  @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.\r
+  @retval EFI_SUCCESS           The variable information is found and returned successfully.\r
+  @retval EFI_UNSUPPORTED       No variable inoformation exists in variable driver. The \r
+                                PcdVariableCollectStatistics should be set TRUE to support it.\r
+  @retval EFI_BUFFER_TOO_SMALL  The buffer is too small to hold the next variable information.\r
+  @retval EFI_INVALID_PARAMETER Input parameter is invalid.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -265,14 +313,17 @@ SmmVariableGetStatistics (
   UINTN                                                StatisticsInfoSize;\r
   CHAR16                                               *InfoName;\r
  \r
-  ASSERT (InfoEntry != NULL);\r
+  if (InfoEntry == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
   VariableInfo = gVariableInfo; \r
   if (VariableInfo == NULL) {\r
     return EFI_UNSUPPORTED;\r
   }\r
 \r
   StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);\r
-  if (*InfoSize < sizeof (VARIABLE_INFO_ENTRY)) {\r
+  if (*InfoSize < StatisticsInfoSize) {\r
     *InfoSize = StatisticsInfoSize;\r
     return EFI_BUFFER_TOO_SMALL;\r
   }\r
@@ -334,6 +385,12 @@ SmmVariableGetStatistics (
 \r
   This SMI handler provides services for the variable wrapper driver.\r
 \r
+  Caution: This function may receive untrusted input.\r
+  This variable data and communicate buffer are external input, so this function will do basic validation.\r
+  Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(), \r
+  VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), \r
+  SmmVariableGetStatistics() should also do validation based on its own knowledge.\r
+\r
   @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().\r
   @param[in]     RegisterContext Points to an optional handler context which was specified when the\r
                                  handler was registered.\r
@@ -348,6 +405,7 @@ SmmVariableGetStatistics (
   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still \r
                                               be called.\r
   @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.\r
+\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
@@ -366,12 +424,39 @@ SmmVariableHandler (
   VARIABLE_INFO_ENTRY                              *VariableInfo;\r
   UINTN                                            InfoSize;\r
 \r
-  ASSERT (CommBuffer != NULL);\r
+  //\r
+  // If input is invalid, stop processing this SMI\r
+  //\r
+  if (CommBuffer == NULL || CommBufferSize == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
 \r
+  if (*CommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBuffer, *CommBufferSize)) {\r
+    DEBUG ((EFI_D_ERROR, "SMM communication buffer size is in SMRAM!\n"));\r
+    return EFI_SUCCESS;\r
+  }\r
+  \r
   SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;\r
+    \r
   switch (SmmVariableFunctionHeader->Function) {\r
     case SMM_VARIABLE_FUNCTION_GET_VARIABLE:\r
-      SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data;     \r
+      SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data;\r
+      InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) \r
+                 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;\r
+\r
+      //\r
+      // SMRAM range check already covered before\r
+      //\r
+      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
+        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));\r
+        Status = EFI_ACCESS_DENIED;\r
+        goto EXIT;\r
+      }\r
+\r
       Status = VariableServiceGetVariable (\r
                  SmmVariableHeader->Name,\r
                  &SmmVariableHeader->Guid,\r
@@ -383,6 +468,17 @@ SmmVariableHandler (
       \r
     case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:\r
       GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) SmmVariableFunctionHeader->Data;\r
+      InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;\r
+\r
+      //\r
+      // SMRAM range check already covered before\r
+      //\r
+      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
+        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));\r
+        Status = EFI_ACCESS_DENIED;\r
+        goto EXIT;\r
+      }\r
+\r
       Status = VariableServiceGetNextVariableName (\r
                  &GetNextVariableName->NameSize,\r
                  GetNextVariableName->Name,\r
@@ -403,6 +499,17 @@ SmmVariableHandler (
       \r
     case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:\r
       QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;\r
+      InfoSize = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO);\r
+\r
+      //\r
+      // SMRAM range check already covered before\r
+      //\r
+      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
+        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));\r
+        Status = EFI_ACCESS_DENIED;\r
+        goto EXIT;\r
+      }\r
+  \r
       Status = VariableServiceQueryVariableInfo (\r
                  QueryVariableInfo->Attributes,\r
                  &QueryVariableInfo->MaximumVariableStorageSize,\r
@@ -412,6 +519,10 @@ SmmVariableHandler (
       break;\r
 \r
     case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:\r
+      if (AtRuntime()) {\r
+        Status = EFI_UNSUPPORTED;\r
+        break;\r
+      }\r
       ReclaimForOS ();\r
       Status = EFI_SUCCESS;\r
       break;\r
@@ -423,18 +534,30 @@ SmmVariableHandler (
 \r
     case SMM_VARIABLE_FUNCTION_GET_STATISTICS:\r
       VariableInfo = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;\r
-      InfoSize = *CommBufferSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data);\r
+      InfoSize = *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;\r
+\r
+      //\r
+      // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here. \r
+      // It is covered by previous CommBuffer check \r
+      //\r
+     \r
+      if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBufferSize, sizeof(UINTN))) {\r
+        DEBUG ((EFI_D_ERROR, "SMM communication buffer size is in SMRAM!\n"));\r
+        Status = EFI_ACCESS_DENIED;\r
+        goto EXIT;\r
+      }  \r
+\r
       Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);\r
-      *CommBufferSize = InfoSize + OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data);\r
+      *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;\r
       break;\r
 \r
     default:\r
-      ASSERT (FALSE);\r
       Status = EFI_UNSUPPORTED;\r
   }\r
 \r
-  SmmVariableFunctionHeader->ReturnStatus = Status;\r
+EXIT:\r
 \r
+  SmmVariableFunctionHeader->ReturnStatus = Status;\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -532,7 +655,9 @@ VariableServiceInitialize (
   EFI_STATUS                              Status;\r
   EFI_HANDLE                              VariableHandle;\r
   VOID                                    *SmmFtwRegistration;\r
-  \r
+  EFI_SMM_ACCESS2_PROTOCOL                *SmmAccess;\r
+  UINTN                                   Size;\r
+\r
   //\r
   // Variable initialize.\r
   //\r
@@ -551,6 +676,28 @@ VariableServiceInitialize (
                     );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
+  //\r
+  // Get SMRAM information\r
+  //\r
+  Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Size = 0;\r
+  Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  Status = gSmst->SmmAllocatePool (\r
+                    EfiRuntimeServicesData,\r
+                    Size,\r
+                    (VOID **)&mSmramRanges\r
+                    );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);\r
+\r
   ///\r
   /// Register SMM variable SMI handler\r
   ///\r