X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;ds=sidebyside;f=MdeModulePkg%2FUniversal%2FVariable%2FRuntimeDxe%2FVariableSmmRuntimeDxe.c;h=e209d54755ef1bec8f1b47ffa2b20f0533b1915d;hb=dc9bd6ed281fcba5358f3004632bdbda968be1e5;hp=3e4330508eb80234b0cd5c572516358a7317514b;hpb=5c7fa429411d336f7c8cafa43b0f7cc879f107dd;p=mirror_edk2.git diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c index 3e4330508e..e209d54755 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -1,23 +1,35 @@ /** @file - Implement all four UEFI Runtime Variable services for the nonvolatile and volatile storage space and install variable architecture protocol based on SMM variable module. -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php + Caution: This module requires additional review when modified. + This driver will have external input - variable data. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + RuntimeServiceGetVariable() and RuntimeServiceSetVariable() are external API + to receive data buffer. The size should be checked carefully. -**/ + InitCommunicateBuffer() is really function to check the variable data size. + +Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include #include #include #include +#include +#include +#include #include #include @@ -26,22 +38,92 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include -#include #include #include #include -#include "VariableSmmCommon.h" +#include -EFI_HANDLE mHandle = NULL; +EFI_HANDLE mHandle = NULL; EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL; EFI_EVENT mVirtualAddressChangeEvent = NULL; EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL; UINT8 *mVariableBuffer = NULL; UINT8 *mVariableBufferPhysical = NULL; -EFI_GUID mSmmVariableWriteGuid = EFI_SMM_VARIABLE_WRITE_GUID; UINTN mVariableBufferSize; +UINTN mVariableBufferPayloadSize; +EFI_LOCK mVariableServicesLock; +EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; +EDKII_VAR_CHECK_PROTOCOL mVarCheck; + +/** + SecureBoot Hook for SetVariable. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. +**/ +VOID +EFIAPI +SecureBootHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + +/** + Some Secure Boot Policy Variable may update following other variable changes(SecureBoot follows PK change, etc). + Record their initial State when variable write service is ready. + +**/ +VOID +EFIAPI +RecordSecureBootPolicyVarData( + VOID + ); + +/** + Acquires lock only at boot time. Simply returns at runtime. + + This is a temperary function that will be removed when + EfiAcquireLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiAcquireLock() at boot time, and simply returns + at runtime. + + @param Lock A pointer to the lock to acquire. + +**/ +VOID +AcquireLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!EfiAtRuntime ()) { + EfiAcquireLock (Lock); + } +} + +/** + Releases lock only at boot time. Simply returns at runtime. + + This is a temperary function which will be removed when + EfiReleaseLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiReleaseLock() at boot time and simply returns + at runtime. + + @param Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!EfiAtRuntime ()) { + EfiReleaseLock (Lock); + } +} /** Initialize the communicate buffer using DataSize and Function. @@ -49,10 +131,13 @@ UINTN mVariableBufferSize; The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + DataSize. + Caution: This function may receive untrusted input. + The data size external input, so this function will validate it carefully to avoid buffer overflow. + @param[out] DataPtr Points to the data in the communicate buffer. @param[in] DataSize The data size to send to SMM. @param[in] Function The function number to initialize the communicate header. - + @retval EFI_INVALID_PARAMETER The data size is too big. @retval EFI_SUCCESS Find the specified variable. @@ -64,10 +149,10 @@ InitCommunicateBuffer ( IN UINTN Function ) { - EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; - SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + - if (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE > mVariableBufferSize) { return EFI_INVALID_PARAMETER; } @@ -75,7 +160,7 @@ InitCommunicateBuffer ( SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) mVariableBuffer; CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); SmmCommunicateHeader->MessageLength = DataSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; - + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; SmmVariableFunctionHeader->Function = Function; if (DataPtr != NULL) { @@ -91,9 +176,9 @@ InitCommunicateBuffer ( @param[in] DataSize This size of the function header and the data. - @RetVal EFI_SUCCESS Success is returned from the functin in SMM. - @RetVal Others Failure is returned from the function in SMM. - + @retval EFI_SUCCESS Success is returned from the functin in SMM. + @retval Others Failure is returned from the function in SMM. + **/ EFI_STATUS SendCommunicateBuffer ( @@ -102,9 +187,9 @@ SendCommunicateBuffer ( { EFI_STATUS Status; UINTN CommSize; - EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; - + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; Status = mSmmCommunication->Communicate (mSmmCommunication, mVariableBufferPhysical, &CommSize); ASSERT_EFI_ERROR (Status); @@ -114,17 +199,262 @@ SendCommunicateBuffer ( return SmmVariableFunctionHeader->ReturnStatus; } +/** + Mark a variable that will become read-only after leaving the DXE phase of execution. + + @param[in] This The VARIABLE_LOCK_PROTOCOL instance. + @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. + @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. + + @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked + as pending to be read-only. + @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. + Or VariableName is an empty string. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request. +**/ +EFI_STATUS +EFIAPI +VariableLockRequestToLock ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; + + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (VariableName); + VariableToLock = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &VariableToLock, PayloadSize, SMM_VARIABLE_FUNCTION_LOCK_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (VariableToLock != NULL); + + CopyGuid (&VariableToLock->Guid, VendorGuid); + VariableToLock->NameSize = VariableNameSize; + CopyMem (VariableToLock->Name, VariableName, VariableToLock->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + +/** + Register SetVariable check handler. + + @param[in] Handler Pointer to check handler. + + @retval EFI_SUCCESS The SetVariable check handler was registered successfully. + @retval EFI_INVALID_PARAMETER Handler is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. + @retval EFI_UNSUPPORTED This interface is not implemented. + For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. + +**/ +EFI_STATUS +EFIAPI +VarCheckRegisterSetVariableCheckHandler ( + IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Variable property set. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] VariableProperty Pointer to the input variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, + or the fields of VariableProperty are not valid. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertySet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty->Revision != VAR_CHECK_VARIABLE_PROPERTY_REVISION) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (Name); + CommVariableProperty = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (CommVariableProperty != NULL); + + CopyGuid (&CommVariableProperty->Guid, Guid); + CopyMem (&CommVariableProperty->VariableProperty, VariableProperty, sizeof (*VariableProperty)); + CommVariableProperty->NameSize = VariableNameSize; + CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + +/** + Variable property get. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[out] VariableProperty Pointer to the output variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. + @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertyGet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (Name); + CommVariableProperty = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (CommVariableProperty != NULL); + + CopyGuid (&CommVariableProperty->Guid, Guid); + CommVariableProperty->NameSize = VariableNameSize; + CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + if (Status == EFI_SUCCESS) { + CopyMem (VariableProperty, &CommVariableProperty->VariableProperty, sizeof (*VariableProperty)); + } + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} /** This code finds variable in storage blocks (Volatile or Non-Volatile). + Caution: This function may receive untrusted input. + The data size is external input, so this function will validate it carefully to avoid buffer overflow. + @param[in] VariableName Name of Variable to be found. @param[in] VendorGuid Variable vendor GUID. @param[out] Attributes Attribute value of the variable found. @param[in, out] DataSize Size of Data found. If size is less than the data, this value contains the required size. @param[out] Data Data pointer. - + @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SUCCESS Find the specified variable. @retval EFI_NOT_FOUND Not found. @@ -144,29 +474,47 @@ RuntimeServiceGetVariable ( EFI_STATUS Status; UINTN PayloadSize; SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + UINTN TempDataSize; + UINTN VariableNameSize; if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { return EFI_INVALID_PARAMETER; } - if ((*DataSize != 0) && (Data == NULL)) { + TempDataSize = *DataSize; + VariableNameSize = StrSize (VariableName); + SmmVariableHeader = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) { return EFI_INVALID_PARAMETER; } - + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + // // Init the communicate buffer. The buffer data size is: // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. // - PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + StrSize (VariableName); + if (TempDataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize) { + // + // If output data buffer exceed SMM payload limit. Trim output buffer to SMM payload size + // + TempDataSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize; + } + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize; + Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); if (EFI_ERROR (Status)) { - return Status; + goto Done; } ASSERT (SmmVariableHeader != NULL); CopyGuid (&SmmVariableHeader->Guid, VendorGuid); - SmmVariableHeader->DataSize = *DataSize; - SmmVariableHeader->NameSize = StrSize (VariableName); + SmmVariableHeader->DataSize = TempDataSize; + SmmVariableHeader->NameSize = VariableNameSize; if (Attributes == NULL) { SmmVariableHeader->Attributes = 0; } else { @@ -182,17 +530,29 @@ RuntimeServiceGetVariable ( // // Get data from SMM. // - *DataSize = SmmVariableHeader->DataSize; + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + // + // SMM CommBuffer DataSize can be a trimed value + // Only update DataSize when needed + // + *DataSize = SmmVariableHeader->DataSize; + } if (Attributes != NULL) { *Attributes = SmmVariableHeader->Attributes; } if (EFI_ERROR (Status)) { - return Status; + goto Done; } - CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize); + if (Data != NULL) { + CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize); + } else { + Status = EFI_INVALID_PARAMETER; + } +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); return Status; } @@ -221,25 +581,60 @@ RuntimeServiceGetNextVariableName ( EFI_STATUS Status; UINTN PayloadSize; SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName; + UINTN OutVariableNameSize; + UINTN InVariableNameSize; if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) { return EFI_INVALID_PARAMETER; } - + + OutVariableNameSize = *VariableNameSize; + InVariableNameSize = StrSize (VariableName); + SmmGetNextVariableName = NULL; + + // + // If input string exceeds SMM payload limit. Return failure + // + if (InVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + // // Init the communicate buffer. The buffer data size is: // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. // - PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + *VariableNameSize; + if (OutVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + // + // If output buffer exceed SMM payload limit. Trim output buffer to SMM payload size + // + OutVariableNameSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name); + } + // + // Payload should be Guid + NameSize + MAX of Input & Output buffer + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + MAX (OutVariableNameSize, InVariableNameSize); + Status = InitCommunicateBuffer ((VOID **)&SmmGetNextVariableName, PayloadSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); if (EFI_ERROR (Status)) { - return Status; + goto Done; } ASSERT (SmmGetNextVariableName != NULL); - SmmGetNextVariableName->NameSize = *VariableNameSize; + // + // SMM comm buffer->NameSize is buffer size for return string + // + SmmGetNextVariableName->NameSize = OutVariableNameSize; + CopyGuid (&SmmGetNextVariableName->Guid, VendorGuid); - CopyMem (SmmGetNextVariableName->Name, VariableName, *VariableNameSize); + // + // Copy whole string + // + CopyMem (SmmGetNextVariableName->Name, VariableName, InVariableNameSize); + if (OutVariableNameSize > InVariableNameSize) { + ZeroMem ((UINT8 *) SmmGetNextVariableName->Name + InVariableNameSize, OutVariableNameSize - InVariableNameSize); + } // // Send data to SMM @@ -249,20 +644,31 @@ RuntimeServiceGetNextVariableName ( // // Get data from SMM. // - *VariableNameSize = SmmGetNextVariableName->NameSize; + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + // + // SMM CommBuffer NameSize can be a trimed value + // Only update VariableNameSize when needed + // + *VariableNameSize = SmmGetNextVariableName->NameSize; + } if (EFI_ERROR (Status)) { - return Status; + goto Done; } - + CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid); - CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); + CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); return Status; } /** This code sets variable in storage blocks (Volatile or Non-Volatile). + Caution: This function may receive untrusted input. + The data size and data are external input, so this function will validate it carefully to avoid buffer overflow. + @param[in] VariableName Name of Variable to be found. @param[in] VendorGuid Variable vendor GUID. @param[in] Attributes Attribute value of the variable found @@ -288,34 +694,48 @@ RuntimeServiceSetVariable ( ) { EFI_STATUS Status; - UINTN PayloadSize; + UINTN PayloadSize; SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; - + UINTN VariableNameSize; + // // Check input parameters. // if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { return EFI_INVALID_PARAMETER; - } + } if (DataSize != 0 && Data == NULL) { return EFI_INVALID_PARAMETER; } - + + VariableNameSize = StrSize (VariableName); + SmmVariableHeader = NULL; + + // + // If VariableName or DataSize exceeds SMM payload limit. Return failure + // + if ((VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) || + (DataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize)){ + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + // // Init the communicate buffer. The buffer data size is: // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. // - PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + StrSize (VariableName) + DataSize; + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + DataSize; Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_SET_VARIABLE); if (EFI_ERROR (Status)) { - return Status; + goto Done; } ASSERT (SmmVariableHeader != NULL); CopyGuid ((EFI_GUID *) &SmmVariableHeader->Guid, VendorGuid); SmmVariableHeader->DataSize = DataSize; - SmmVariableHeader->NameSize = StrSize (VariableName); + SmmVariableHeader->NameSize = VariableNameSize; SmmVariableHeader->Attributes = Attributes; CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); CopyMem ((UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize, Data, DataSize); @@ -324,7 +744,18 @@ RuntimeServiceSetVariable ( // Send data to SMM. // Status = SendCommunicateBuffer (PayloadSize); - + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + + if (!EfiAtRuntime ()) { + if (!EFI_ERROR (Status)) { + SecureBootHook ( + VariableName, + VendorGuid + ); + } + } return Status; } @@ -359,18 +790,22 @@ RuntimeServiceQueryVariableInfo ( UINTN PayloadSize; SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *SmmQueryVariableInfo; + SmmQueryVariableInfo = NULL; + if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) { return EFI_INVALID_PARAMETER; } - + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + // // Init the communicate buffer. The buffer data size is: // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize; // - PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY); + PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); Status = InitCommunicateBuffer ((VOID **)&SmmQueryVariableInfo, PayloadSize, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO); if (EFI_ERROR (Status)) { - return Status; + goto Done; } ASSERT (SmmQueryVariableInfo != NULL); @@ -381,7 +816,7 @@ RuntimeServiceQueryVariableInfo ( // Status = SendCommunicateBuffer (PayloadSize); if (EFI_ERROR (Status)) { - return Status; + goto Done; } // @@ -389,9 +824,11 @@ RuntimeServiceQueryVariableInfo ( // *MaximumVariableSize = SmmQueryVariableInfo->MaximumVariableSize; *MaximumVariableStorageSize = SmmQueryVariableInfo->MaximumVariableStorageSize; - *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; - - return EFI_SUCCESS; + *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; } @@ -415,7 +852,7 @@ OnExitBootServices ( // Init the communicate buffer. The buffer data size is: // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. // - InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE); + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE); // // Send data to SMM. @@ -445,11 +882,13 @@ OnReadyToBoot ( // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. // InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT); - + // // Send data to SMM. // SendCommunicateBuffer (0); + + gBS->CloseEvent (Event); } @@ -474,13 +913,86 @@ VariableAddressChangeEvent ( EfiConvertPointer (0x0, (VOID **) &mSmmCommunication); } +/** + This code gets variable payload size. + + @param[out] VariablePayloadSize Output pointer to variable payload size. + + @retval EFI_SUCCESS Get successfully. + @retval Others Get unsuccessfully. + +**/ +EFI_STATUS +EFIAPI +GetVariablePayloadSize ( + OUT UINTN *VariablePayloadSize + ) +{ + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *SmmGetPayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + UINTN CommSize; + UINT8 *CommBuffer; + + SmmGetPayloadSize = NULL; + CommBuffer = NULL; + + if(VariablePayloadSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + // + CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + CommBuffer = AllocateZeroPool (CommSize); + if (CommBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE; + SmmGetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data; + + // + // Send data to SMM. + // + Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize); + ASSERT_EFI_ERROR (Status); + + Status = SmmVariableFunctionHeader->ReturnStatus; + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data from SMM. + // + *VariablePayloadSize = SmmGetPayloadSize->VariablePayloadSize; + +Done: + if (CommBuffer != NULL) { + FreePool (CommBuffer); + } + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} /** Initialize variable service and install Variable Architectural protocol. @param[in] Event Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. - + **/ VOID EFIAPI @@ -495,15 +1007,16 @@ SmmVariableReady ( if (EFI_ERROR (Status)) { return; } - + Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication); ASSERT_EFI_ERROR (Status); - + // - // Allocate memory for variable store. + // Allocate memory for variable communicate buffer. // - mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; - mVariableBufferSize += MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)); + Status = GetVariablePayloadSize (&mVariableBufferPayloadSize); + ASSERT_EFI_ERROR (Status); + mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + mVariableBufferPayloadSize; mVariableBuffer = AllocateRuntimePool (mVariableBufferSize); ASSERT (mVariableBuffer != NULL); @@ -516,17 +1029,39 @@ SmmVariableReady ( gRT->GetNextVariableName = RuntimeServiceGetNextVariableName; gRT->SetVariable = RuntimeServiceSetVariable; gRT->QueryVariableInfo = RuntimeServiceQueryVariableInfo; - + // // Install the Variable Architectural Protocol on a new handle. // Status = gBS->InstallProtocolInterface ( &mHandle, - &gEfiVariableArchProtocolGuid, + &gEfiVariableArchProtocolGuid, EFI_NATIVE_INTERFACE, NULL ); ASSERT_EFI_ERROR (Status); + + mVariableLock.RequestToLock = VariableLockRequestToLock; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVariableLockProtocolGuid, + &mVariableLock, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mVarCheck.RegisterSetVariableCheckHandler = VarCheckRegisterSetVariableCheckHandler; + mVarCheck.VariablePropertySet = VarCheckVariablePropertySet; + mVarCheck.VariablePropertyGet = VarCheckVariablePropertyGet; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVarCheckProtocolGuid, + &mVarCheck, + NULL + ); + ASSERT_EFI_ERROR (Status); + + gBS->CloseEvent (Event); } @@ -535,7 +1070,7 @@ SmmVariableReady ( @param[in] Event Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. - + **/ VOID EFIAPI @@ -550,30 +1085,38 @@ SmmVariableWriteReady ( // // Check whether the protocol is installed or not. // - Status = gBS->LocateProtocol (&mSmmVariableWriteGuid, NULL, (VOID **) &ProtocolOps); + Status = gBS->LocateProtocol (&gSmmVariableWriteGuid, NULL, (VOID **) &ProtocolOps); if (EFI_ERROR (Status)) { return; } - + + // + // Some Secure Boot Policy Var (SecureBoot, etc) updates following other + // Secure Boot Policy Variable change. Record their initial value. + // + RecordSecureBootPolicyVarData(); + Status = gBS->InstallProtocolInterface ( &mHandle, - &gEfiVariableWriteArchProtocolGuid, + &gEfiVariableWriteArchProtocolGuid, EFI_NATIVE_INTERFACE, NULL ); - ASSERT_EFI_ERROR (Status); + ASSERT_EFI_ERROR (Status); + + gBS->CloseEvent (Event); } /** Variable Driver main entry point. The Variable driver places the 4 EFI - runtime services in the EFI System Table and installs arch protocols - for variable read and write services being availible. It also registers + runtime services in the EFI System Table and installs arch protocols + for variable read and write services being available. It also registers a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. - @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. - + @retval EFI_SUCCESS Variable service successfully initialized. **/ @@ -588,15 +1131,18 @@ VariableSmmRuntimeInitialize ( VOID *SmmVariableWriteRegistration; EFI_EVENT OnReadyToBootEvent; EFI_EVENT ExitBootServiceEvent; - + EFI_EVENT LegacyBootEvent; + + EfiInitializeLock (&mVariableServicesLock, TPL_NOTIFY); + // // Smm variable service is ready // EfiCreateProtocolNotifyEvent ( - &gEfiSmmVariableProtocolGuid, - TPL_CALLBACK, - SmmVariableReady, - NULL, + &gEfiSmmVariableProtocolGuid, + TPL_CALLBACK, + SmmVariableReady, + NULL, &SmmVariableRegistration ); @@ -604,10 +1150,10 @@ VariableSmmRuntimeInitialize ( // Smm Non-Volatile variable write service is ready // EfiCreateProtocolNotifyEvent ( - &mSmmVariableWriteGuid, - TPL_CALLBACK, - SmmVariableWriteReady, - NULL, + &gSmmVariableWriteGuid, + TPL_CALLBACK, + SmmVariableWriteReady, + NULL, &SmmVariableWriteRegistration ); @@ -615,11 +1161,11 @@ VariableSmmRuntimeInitialize ( // Register the event to reclaim variable for OS usage. // EfiCreateEventReadyToBootEx ( - TPL_NOTIFY, - OnReadyToBoot, - NULL, + TPL_NOTIFY, + OnReadyToBoot, + NULL, &OnReadyToBootEvent - ); + ); // // Register the event to inform SMM variable that it is at runtime. @@ -631,7 +1177,18 @@ VariableSmmRuntimeInitialize ( NULL, &gEfiEventExitBootServicesGuid, &ExitBootServiceEvent - ); + ); + + // + // Register the event to inform SMM variable that it is at runtime for legacy boot. + // Reuse OnExitBootServices() here. + // + EfiCreateEventLegacyBootEx( + TPL_NOTIFY, + OnExitBootServices, + NULL, + &LegacyBootEvent + ); // // Register the event to convert the pointer for runtime. @@ -644,7 +1201,7 @@ VariableSmmRuntimeInitialize ( &gEfiEventVirtualAddressChangeGuid, &mVirtualAddressChangeEvent ); - + return EFI_SUCCESS; }