| /** @file | |
| This is a host-based unit test for the VariableLockRequestToLock shim. | |
| Copyright (c) Microsoft Corporation. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include <stddef.h> | |
| #include <setjmp.h> | |
| #include <cmocka.h> | |
| #include <Uefi.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UnitTestLib.h> | |
| #include <Library/VariablePolicyLib.h> | |
| #include <Library/VariablePolicyHelperLib.h> | |
| #include <Protocol/VariableLock.h> | |
| #define UNIT_TEST_NAME "VarPol/VarLock Shim Unit Test" | |
| #define UNIT_TEST_VERSION "1.0" | |
| /// === CODE UNDER TEST =========================================================================== | |
| EFI_STATUS | |
| EFIAPI | |
| VariableLockRequestToLock ( | |
| IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, | |
| IN CHAR16 *VariableName, | |
| IN EFI_GUID *VendorGuid | |
| ); | |
| /// === TEST DATA ================================================================================== | |
| // | |
| // Test GUID 1 {F955BA2D-4A2C-480C-BFD1-3CC522610592} | |
| // | |
| EFI_GUID mTestGuid1 = { | |
| 0xf955ba2d, 0x4a2c, 0x480c, { 0xbf, 0xd1, 0x3c, 0xc5, 0x22, 0x61, 0x5, 0x92 } | |
| }; | |
| // | |
| // Test GUID 2 {2DEA799E-5E73-43B9-870E-C945CE82AF3A} | |
| // | |
| EFI_GUID mTestGuid2 = { | |
| 0x2dea799e, 0x5e73, 0x43b9, { 0x87, 0xe, 0xc9, 0x45, 0xce, 0x82, 0xaf, 0x3a } | |
| }; | |
| // | |
| // Test GUID 3 {698A2BFD-A616-482D-B88C-7100BD6682A9} | |
| // | |
| EFI_GUID mTestGuid3 = { | |
| 0x698a2bfd, 0xa616, 0x482d, { 0xb8, 0x8c, 0x71, 0x0, 0xbd, 0x66, 0x82, 0xa9 } | |
| }; | |
| #define TEST_VAR_1_NAME L"TestVar1" | |
| #define TEST_VAR_2_NAME L"TestVar2" | |
| #define TEST_VAR_3_NAME L"TestVar3" | |
| #define TEST_POLICY_ATTRIBUTES_NULL 0 | |
| #define TEST_POLICY_MIN_SIZE_NULL 0 | |
| #define TEST_POLICY_MAX_SIZE_NULL MAX_UINT32 | |
| #define TEST_POLICY_MIN_SIZE_10 10 | |
| #define TEST_POLICY_MAX_SIZE_200 200 | |
| /// === HELPER FUNCTIONS =========================================================================== | |
| /** | |
| Mocked version of GetVariable, for testing. | |
| @param VariableName | |
| @param VendorGuid | |
| @param Attributes | |
| @param DataSize | |
| @param Data | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| StubGetVariableNull ( | |
| IN CHAR16 *VariableName, | |
| IN EFI_GUID *VendorGuid, | |
| OUT UINT32 *Attributes OPTIONAL, | |
| IN OUT UINTN *DataSize, | |
| OUT VOID *Data OPTIONAL | |
| ) | |
| { | |
| UINT32 MockedAttr; | |
| UINTN MockedDataSize; | |
| VOID *MockedData; | |
| EFI_STATUS MockedReturn; | |
| check_expected_ptr (VariableName); | |
| check_expected_ptr (VendorGuid); | |
| check_expected_ptr (DataSize); | |
| MockedAttr = (UINT32)mock (); | |
| MockedDataSize = (UINTN)mock (); | |
| MockedData = (VOID *)(UINTN)mock (); | |
| MockedReturn = (EFI_STATUS)mock (); | |
| if (Attributes != NULL) { | |
| *Attributes = MockedAttr; | |
| } | |
| if ((Data != NULL) && !EFI_ERROR (MockedReturn)) { | |
| CopyMem (Data, MockedData, MockedDataSize); | |
| } | |
| *DataSize = MockedDataSize; | |
| return MockedReturn; | |
| } | |
| // | |
| // Anything you think might be helpful that isn't a test itself. | |
| // | |
| /** | |
| This is a common setup function that will ensure the library is always | |
| initialized with the stubbed GetVariable. | |
| Not used by all test cases, but by most. | |
| @param[in] Context Unit test case context | |
| **/ | |
| STATIC | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LibInitMocked ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| return EFI_ERROR (InitVariablePolicyLib (StubGetVariableNull)) ? UNIT_TEST_ERROR_PREREQUISITE_NOT_MET : UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Common cleanup function to make sure that the library is always de-initialized | |
| prior to the next test case. | |
| @param[in] Context Unit test case context | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| LibCleanup ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| DeinitVariablePolicyLib (); | |
| } | |
| /// === TEST CASES ================================================================================= | |
| /// ===== SHIM SUITE =========================================================== | |
| /** | |
| Test Case that locks a single variable using the Variable Lock Protocol. | |
| The call is expected to succeed. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LockingWithoutAnyPoliciesShouldSucceed ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Test Case that locks the same variable twice using the Variable Lock Protocol. | |
| Both calls are expected to succeed. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LockingTwiceShouldSucceed ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Test Case that locks a variable using the Variable Policy Protocol then locks | |
| the same variable using the Variable Lock Protocol. | |
| Both calls are expected to succeed. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LockingALockedVariableShouldSucceed ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VARIABLE_POLICY_ENTRY *NewEntry; | |
| // | |
| // Create a variable policy that locks the variable. | |
| // | |
| Status = CreateBasicVariablePolicy ( | |
| &mTestGuid1, | |
| TEST_VAR_1_NAME, | |
| TEST_POLICY_MIN_SIZE_NULL, | |
| TEST_POLICY_MAX_SIZE_200, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| VARIABLE_POLICY_TYPE_LOCK_NOW, | |
| &NewEntry | |
| ); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| // | |
| // Register the new policy. | |
| // | |
| Status = RegisterVariablePolicy (NewEntry); | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| FreePool (NewEntry); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Test Case that locks a variable using the Variable Policy Protocol with a | |
| policy other than LOCK_NOW then attempts to lock the same variable using the | |
| Variable Lock Protocol. The call to Variable Policy is expected to succeed | |
| and the call to Variable Lock is expected to fail. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LockingAnUnlockedVariableShouldFail ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VARIABLE_POLICY_ENTRY *NewEntry; | |
| // Create a variable policy that locks the variable. | |
| Status = CreateVarStateVariablePolicy ( | |
| &mTestGuid1, | |
| TEST_VAR_1_NAME, | |
| TEST_POLICY_MIN_SIZE_NULL, | |
| TEST_POLICY_MAX_SIZE_200, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| &mTestGuid2, | |
| 1, | |
| TEST_VAR_2_NAME, | |
| &NewEntry | |
| ); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| // Register the new policy. | |
| Status = RegisterVariablePolicy (NewEntry); | |
| // Configure the stub to not care about parameters. We're testing errors. | |
| expect_any_always (StubGetVariableNull, VariableName); | |
| expect_any_always (StubGetVariableNull, VendorGuid); | |
| expect_any_always (StubGetVariableNull, DataSize); | |
| // With a policy, make sure that writes still work, since the variable doesn't exist. | |
| will_return (StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL); // Attributes | |
| will_return (StubGetVariableNull, 0); // Size | |
| will_return (StubGetVariableNull, (UINTN)NULL); // DataPtr | |
| will_return (StubGetVariableNull, EFI_NOT_FOUND); // Status | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_TRUE (EFI_ERROR (Status)); | |
| FreePool (NewEntry); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Test Case that locks a variable using the Variable Policy Protocol with a | |
| policy other than LOCK_NOW, but is currently locked. Then attempts to lock | |
| the same variable using the Variable Lock Protocol. The call to Variable | |
| Policy is expected to succeed and the call to Variable Lock also expected to | |
| succeed. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LockingALockedVariableWithMatchingDataShouldSucceed ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VARIABLE_POLICY_ENTRY *NewEntry; | |
| UINT8 Data; | |
| // Create a variable policy that locks the variable. | |
| Status = CreateVarStateVariablePolicy ( | |
| &mTestGuid1, | |
| TEST_VAR_1_NAME, | |
| TEST_POLICY_MIN_SIZE_NULL, | |
| TEST_POLICY_MAX_SIZE_200, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| &mTestGuid2, | |
| 1, | |
| TEST_VAR_2_NAME, | |
| &NewEntry | |
| ); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| // Register the new policy. | |
| Status = RegisterVariablePolicy (NewEntry); | |
| // Configure the stub to not care about parameters. We're testing errors. | |
| expect_any_always (StubGetVariableNull, VariableName); | |
| expect_any_always (StubGetVariableNull, VendorGuid); | |
| expect_any_always (StubGetVariableNull, DataSize); | |
| // With a policy, make sure that writes still work, since the variable doesn't exist. | |
| Data = 1; | |
| will_return (StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL); // Attributes | |
| will_return (StubGetVariableNull, sizeof (Data)); // Size | |
| will_return (StubGetVariableNull, (UINTN)&Data); // DataPtr | |
| will_return (StubGetVariableNull, EFI_SUCCESS); // Status | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_TRUE (!EFI_ERROR (Status)); | |
| FreePool (NewEntry); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Test Case that locks a variable using the Variable Policy Protocol with a | |
| policy other than LOCK_NOW, but variable data does not match. Then attempts | |
| to lock the same variable using the Variable Lock Protocol. The call to | |
| Variable Policy is expected to succeed and the call to Variable Lock is | |
| expected to fail. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| LockingALockedVariableWithNonMatchingDataShouldFail ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VARIABLE_POLICY_ENTRY *NewEntry; | |
| UINT8 Data; | |
| // Create a variable policy that locks the variable. | |
| Status = CreateVarStateVariablePolicy ( | |
| &mTestGuid1, | |
| TEST_VAR_1_NAME, | |
| TEST_POLICY_MIN_SIZE_NULL, | |
| TEST_POLICY_MAX_SIZE_200, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| &mTestGuid2, | |
| 1, | |
| TEST_VAR_2_NAME, | |
| &NewEntry | |
| ); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| // Register the new policy. | |
| Status = RegisterVariablePolicy (NewEntry); | |
| // Configure the stub to not care about parameters. We're testing errors. | |
| expect_any_always (StubGetVariableNull, VariableName); | |
| expect_any_always (StubGetVariableNull, VendorGuid); | |
| expect_any_always (StubGetVariableNull, DataSize); | |
| // With a policy, make sure that writes still work, since the variable doesn't exist. | |
| Data = 2; | |
| will_return (StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL); // Attributes | |
| will_return (StubGetVariableNull, sizeof (Data)); // Size | |
| will_return (StubGetVariableNull, (UINTN)&Data); // DataPtr | |
| will_return (StubGetVariableNull, EFI_SUCCESS); // Status | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_TRUE (EFI_ERROR (Status)); | |
| FreePool (NewEntry); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Test Case that locks a variable using Variable Lock Protocol Policy Protocol | |
| then and then attempts to lock the same variable using the Variable Policy | |
| Protocol. The call to Variable Lock is expected to succeed and the call to | |
| Variable Policy is expected to fail. | |
| @param[in] Context Unit test case context | |
| **/ | |
| UNIT_TEST_STATUS | |
| EFIAPI | |
| SettingPolicyForALockedVariableShouldFail ( | |
| IN UNIT_TEST_CONTEXT Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VARIABLE_POLICY_ENTRY *NewEntry; | |
| // Lock the variable. | |
| Status = VariableLockRequestToLock (NULL, TEST_VAR_1_NAME, &mTestGuid1); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| // Create a variable policy that locks the variable. | |
| Status = CreateVarStateVariablePolicy ( | |
| &mTestGuid1, | |
| TEST_VAR_1_NAME, | |
| TEST_POLICY_MIN_SIZE_NULL, | |
| TEST_POLICY_MAX_SIZE_200, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| TEST_POLICY_ATTRIBUTES_NULL, | |
| &mTestGuid2, | |
| 1, | |
| TEST_VAR_2_NAME, | |
| &NewEntry | |
| ); | |
| UT_ASSERT_NOT_EFI_ERROR (Status); | |
| // Register the new policy. | |
| Status = RegisterVariablePolicy (NewEntry); | |
| UT_ASSERT_TRUE (EFI_ERROR (Status)); | |
| FreePool (NewEntry); | |
| return UNIT_TEST_PASSED; | |
| } | |
| /** | |
| Main entry point to this unit test application. | |
| Sets up and runs the test suites. | |
| **/ | |
| VOID | |
| EFIAPI | |
| UnitTestMain ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UNIT_TEST_FRAMEWORK_HANDLE Framework; | |
| UNIT_TEST_SUITE_HANDLE ShimTests; | |
| Framework = NULL; | |
| DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION)); | |
| // | |
| // Start setting up the test framework for running the tests. | |
| // | |
| Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); | |
| goto EXIT; | |
| } | |
| // | |
| // Add all test suites and tests. | |
| // | |
| Status = CreateUnitTestSuite ( | |
| &ShimTests, | |
| Framework, | |
| "Variable Lock Shim Tests", | |
| "VarPolicy.VarLockShim", | |
| NULL, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for ShimTests\n")); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto EXIT; | |
| } | |
| AddTestCase ( | |
| ShimTests, | |
| "Locking a variable with no matching policies should always work", | |
| "EmptyPolicies", | |
| LockingWithoutAnyPoliciesShouldSucceed, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| AddTestCase ( | |
| ShimTests, | |
| "Locking a variable twice should always work", | |
| "DoubleLock", | |
| LockingTwiceShouldSucceed, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| AddTestCase ( | |
| ShimTests, | |
| "Locking a variable that's already locked by another policy should work", | |
| "LockAfterPolicy", | |
| LockingALockedVariableShouldSucceed, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| AddTestCase ( | |
| ShimTests, | |
| "Locking a variable that already has an unlocked policy should fail", | |
| "LockAfterUnlockedPolicy", | |
| LockingAnUnlockedVariableShouldFail, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| AddTestCase ( | |
| ShimTests, | |
| "Locking a variable that already has an locked policy should succeed", | |
| "LockAfterLockedPolicyMatchingData", | |
| LockingALockedVariableWithMatchingDataShouldSucceed, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| AddTestCase ( | |
| ShimTests, | |
| "Locking a variable that already has an locked policy with matching data should succeed", | |
| "LockAfterLockedPolicyNonMatchingData", | |
| LockingALockedVariableWithNonMatchingDataShouldFail, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| AddTestCase ( | |
| ShimTests, | |
| "Adding a policy for a variable that has previously been locked should always fail", | |
| "SetPolicyAfterLock", | |
| SettingPolicyForALockedVariableShouldFail, | |
| LibInitMocked, | |
| LibCleanup, | |
| NULL | |
| ); | |
| // | |
| // Execute the tests. | |
| // | |
| Status = RunAllTestSuites (Framework); | |
| EXIT: | |
| if (Framework != NULL) { | |
| FreeUnitTestFramework (Framework); | |
| } | |
| return; | |
| } | |
| /// | |
| /// Avoid ECC error for function name that starts with lower case letter | |
| /// | |
| #define Main main | |
| /** | |
| Standard POSIX C entry point for host based unit test execution. | |
| @param[in] Argc Number of arguments | |
| @param[in] Argv Array of pointers to arguments | |
| @retval 0 Success | |
| @retval other Error | |
| **/ | |
| INT32 | |
| Main ( | |
| IN INT32 Argc, | |
| IN CHAR8 *Argv[] | |
| ) | |
| { | |
| UnitTestMain (); | |
| return 0; | |
| } |