/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if defined(HAS_GTEST) #include #else #include "gtest_stubs.h" #endif #include #include #include #include "fake_storage.h" namespace nvram { namespace { class NvramManagerTest : public testing::Test { protected: NvramManagerTest() { storage::Clear(); } static void SetupHeader(uint32_t header_version, uint32_t index) { NvramHeader header; header.version = header_version; ASSERT_TRUE(header.allocated_indices.Resize(1)); header.allocated_indices[0] = index; ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header)); } static void ReadAndCompareSpaceData(NvramManager* nvram, uint32_t index, const void* expected_contents, size_t expected_size) { ReadSpaceRequest read_space_request; read_space_request.index = index; ReadSpaceResponse read_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram->ReadSpace(read_space_request, &read_space_response)); ASSERT_EQ(expected_size, read_space_response.buffer.size()); EXPECT_EQ(0, memcmp(read_space_response.buffer.data(), expected_contents, expected_size)); } static uint32_t GetControlsMask(const Vector& controls) { uint32_t mask = 0; for (nvram_control_t control : controls) { mask |= (1 << control); } return mask; } }; TEST_F(NvramManagerTest, Init_FromScratch) { NvramManager nvram; GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 1; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ( NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); } TEST_F(NvramManagerTest, Init_TrailingStorageBytes) { // Set up a pre-existing space and add some trailing bytes. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); Blob space_blob; ASSERT_EQ(storage::Status::kSuccess, storage::LoadSpace(1, &space_blob)); ASSERT_TRUE(space_blob.Resize(space_blob.size() + 10)); ASSERT_EQ(storage::Status::kSuccess, storage::StoreSpace(1, space_blob)); // Produce a matching header and append some trailing bytes. NvramHeader header; header.version = NvramHeader::kVersion; ASSERT_TRUE(header.allocated_indices.Resize(1)); header.allocated_indices[0] = 1; ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header)); Blob header_blob; ASSERT_EQ(storage::Status::kSuccess, storage::LoadHeader(&header_blob)); ASSERT_TRUE(header_blob.Resize(header_blob.size() + 10)); ASSERT_EQ(storage::Status::kSuccess, storage::StoreHeader(header_blob)); // Initialize the |NvramManager| and check that the header and space blobs get // loaded successfully. NvramManager nvram; GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetInfo(get_info_request, &get_info_response)); ASSERT_EQ(1U, get_info_response.space_list.size()); EXPECT_EQ(1U, get_info_response.space_list[0]); GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 1; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10U, get_space_info_response.size); } TEST_F(NvramManagerTest, Init_SpacesPresent) { // Set up two pre-existing spaces. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); ASSERT_TRUE(space.contents.Resize(20)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(2, space)); // Indicate 3 present spaces in the header, including one that doesn't have // space data in storage. NvramHeader header; header.version = NvramHeader::kVersion; ASSERT_TRUE(header.allocated_indices.Resize(3)); header.allocated_indices[0] = 1; header.allocated_indices[1] = 2; header.allocated_indices[2] = 3; header.provisional_index.Activate() = 4; ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header)); NvramManager nvram; // Check that the spaces are correctly recovered. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 1; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10u, get_space_info_response.size); get_space_info_request.index = 2; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(20u, get_space_info_response.size); get_space_info_request.index = 3; EXPECT_EQ( NV_RESULT_INTERNAL_ERROR, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); get_space_info_request.index = 4; EXPECT_EQ( NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); } TEST_F(NvramManagerTest, Init_BadSpacePresent) { // Set up a good and a bad NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); const uint8_t kBadSpaceData[] = {0xba, 0xad}; Blob bad_space_blob; ASSERT_TRUE(bad_space_blob.Assign(kBadSpaceData, sizeof(kBadSpaceData))); ASSERT_EQ(storage::Status::kSuccess, storage::StoreSpace(2, bad_space_blob)); NvramHeader header; header.version = NvramHeader::kVersion; ASSERT_TRUE(header.allocated_indices.Resize(2)); header.allocated_indices[0] = 1; header.allocated_indices[1] = 2; ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header)); NvramManager nvram; // The bad index will fail requests. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 2; GetSpaceInfoResponse get_space_info_response; nvram_result_t result = nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response); EXPECT_NE(NV_RESULT_SUCCESS, result); EXPECT_NE(NV_RESULT_SPACE_DOES_NOT_EXIST, result); // A request to get info for the good index should succeed. get_space_info_request.index = 1; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10u, get_space_info_response.size); } TEST_F(NvramManagerTest, Init_NewerStorageVersion) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); SetupHeader(NvramHeader::kVersion + 1, 1); NvramManager nvram; // Requests should fail due to version mismatch. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 1; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ( NV_RESULT_INTERNAL_ERROR, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); } TEST_F(NvramManagerTest, Init_StorageObjectTypeMismatch) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); // Copy the space blob to the header storage. Blob space_blob; ASSERT_EQ(storage::Status::kSuccess, storage::LoadSpace(1, &space_blob)); ASSERT_EQ(storage::Status::kSuccess, storage::StoreHeader(space_blob)); NvramManager nvram; // Initialization should detect that the header storage object doesn't look // like a header, so initialization should fail. GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.GetInfo(get_info_request, &get_info_response)); } TEST_F(NvramManagerTest, CreateSpace_Success) { NvramManager nvram; // Make a call to CreateSpace, which should succeed. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 32; ASSERT_TRUE(create_space_request.controls.Resize(5)); create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK; create_space_request.controls[1] = NV_CONTROL_BOOT_READ_LOCK; create_space_request.controls[2] = NV_CONTROL_WRITE_AUTHORIZATION; create_space_request.controls[3] = NV_CONTROL_READ_AUTHORIZATION; create_space_request.controls[4] = NV_CONTROL_WRITE_EXTEND; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.CreateSpace(create_space_request, &create_space_response)); // GetSpaceInfo should reflect the space parameters set during creation. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 1; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(32u, get_space_info_response.size); EXPECT_EQ(GetControlsMask(create_space_request.controls), GetControlsMask(get_space_info_response.controls)); EXPECT_EQ(false, get_space_info_response.read_locked); EXPECT_EQ(false, get_space_info_response.write_locked); } TEST_F(NvramManagerTest, CreateSpace_Existing) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); SetupHeader(NvramHeader::kVersion, 1); NvramManager nvram; // A request to create another space with the same index should fail. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_SPACE_ALREADY_EXISTS, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, CreateSpace_TooLarge) { NvramManager nvram; // A request to create a space with a too large content size should fail. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16384; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, CreateSpace_AuthTooLarge) { NvramManager nvram; // A request to create a space with a too large authorization value size // should fail. CreateSpaceRequest create_space_request; create_space_request.index = 1; ASSERT_TRUE(create_space_request.authorization_value.Resize(256)); CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, CreateSpace_BadControl) { NvramManager nvram; // A request to create a space with an unknown control value should fail. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16; ASSERT_TRUE(create_space_request.controls.Resize(2)); create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK; create_space_request.controls[1] = 17; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, CreateSpace_ControlWriteLockExclusive) { NvramManager nvram; // Spaces may not be created with conflicting write lock modes. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16; ASSERT_TRUE(create_space_request.controls.Resize(2)); create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK; create_space_request.controls[1] = NV_CONTROL_PERSISTENT_WRITE_LOCK; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, CreateSpace_WriteExtendSpaceSize) { NvramManager nvram; // Write-extend spaces must match SHA256 hash size, i.e. 32 bytes. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16; ASSERT_TRUE(create_space_request.controls.Resize(1)); create_space_request.controls[0] = NV_CONTROL_WRITE_EXTEND; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, CreateSpace_HeaderWriteError) { // If the header fails to get written to storage, the creation request should // fail. storage::SetHeaderWriteError(true); NvramManager nvram; CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.CreateSpace(create_space_request, &create_space_response)); // The space shouldn't be present. GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetInfo(get_info_request, &get_info_response)); EXPECT_EQ(0U, get_info_response.space_list.size()); // Creation of the space after clearing the error should work. storage::SetHeaderWriteError(false); EXPECT_EQ(NV_RESULT_SUCCESS, nvram.CreateSpace(create_space_request, &create_space_response)); // The space should be reported as allocated now. EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetInfo(get_info_request, &get_info_response)); ASSERT_EQ(1U, get_info_response.space_list.size()); EXPECT_EQ(1U, get_info_response.space_list[0]); } TEST_F(NvramManagerTest, CreateSpace_SpaceWriteError) { storage::SetSpaceWriteError(1, true); NvramManager nvram; // A request to create another space with the same index should fail. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 16; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.CreateSpace(create_space_request, &create_space_response)); // Reloading the state after a crash should not show any traces of the space. storage::SetSpaceWriteError(1, false); NvramManager nvram2; // The space shouldn't exist in the space list. GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram2.GetInfo(get_info_request, &get_info_response)); EXPECT_EQ(0U, get_info_response.space_list.size()); // The space info request should indicate the space doesn't exist. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 1; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ( NV_RESULT_SPACE_DOES_NOT_EXIST, nvram2.GetSpaceInfo(get_space_info_request, &get_space_info_response)); } TEST_F(NvramManagerTest, DeleteSpace_SpaceAbsent) { NvramManager nvram; // Attempt to delete a non-existing space. DeleteSpaceRequest delete_space_request; delete_space_request.index = 42; DeleteSpaceResponse delete_space_response; EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.DeleteSpace(delete_space_request, &delete_space_response)); } TEST_F(NvramManagerTest, DeleteSpace_Success) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space)); SetupHeader(NvramHeader::kVersion, 42); NvramManager nvram; // Successful deletion. DeleteSpaceRequest delete_space_request; delete_space_request.index = 42; DeleteSpaceResponse delete_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DeleteSpace(delete_space_request, &delete_space_response)); } TEST_F(NvramManagerTest, DeleteSpace_AuthorizationFailure) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_WRITE_AUTHORIZATION); const char kAuthorizationValue[] = "secret"; ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue, sizeof(kAuthorizationValue))); ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space)); SetupHeader(NvramHeader::kVersion, 42); NvramManager nvram; // Deletion should fail if correct secret is not provided. DeleteSpaceRequest delete_space_request; delete_space_request.index = 42; DeleteSpaceResponse delete_space_response; EXPECT_EQ(NV_RESULT_ACCESS_DENIED, nvram.DeleteSpace(delete_space_request, &delete_space_response)); } TEST_F(NvramManagerTest, DeleteSpace_HalfDeleted) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space)); SetupHeader(NvramHeader::kVersion, 42); // Hold on to the space data. Blob space_data; ASSERT_EQ(storage::Status::kSuccess, storage::LoadSpace(42, &space_data)); NvramManager nvram; // Delete the space. DeleteSpaceRequest delete_space_request; delete_space_request.index = 42; DeleteSpaceResponse delete_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DeleteSpace(delete_space_request, &delete_space_response)); // Put the space data back into place to simulate a half-completed deletion. ASSERT_EQ(storage::Status::kSuccess, storage::StoreSpace(42, space_data)); // The space should remain deleted after re-initialization. NvramManager nvram2; GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 42; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ( NV_RESULT_SPACE_DOES_NOT_EXIST, nvram2.GetSpaceInfo(get_space_info_request, &get_space_info_response)); // Re-creation of a space with the same index should work. CreateSpaceRequest create_space_request; create_space_request.index = 42; create_space_request.size = 32; ASSERT_TRUE(create_space_request.controls.Resize(1)); create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram2.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, DeleteSpace_SpaceDeleteError) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space)); SetupHeader(NvramHeader::kVersion, 42); // Make space deletion fail. storage::SetSpaceWriteError(42, true); NvramManager nvram; // Attempt to delete the space. DeleteSpaceRequest delete_space_request; delete_space_request.index = 42; DeleteSpaceResponse delete_space_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.DeleteSpace(delete_space_request, &delete_space_response)); // The space should remain present. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 42; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10U, get_space_info_response.size); // Starting up from scratch shouldn't destroy the space either. storage::SetSpaceWriteError(42, false); NvramManager nvram2; GetSpaceInfoResponse get_space_info_response_2; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response_2)); EXPECT_EQ(10U, get_space_info_response_2.size); } TEST_F(NvramManagerTest, DeleteSpace_HeaderWriteError) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space)); SetupHeader(NvramHeader::kVersion, 42); // Header write on deletion will fail. storage::SetHeaderWriteError(true); NvramManager nvram; // Attempt to delete the space. DeleteSpaceRequest delete_space_request; delete_space_request.index = 42; DeleteSpaceResponse delete_space_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.DeleteSpace(delete_space_request, &delete_space_response)); // The space should remain present. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 42; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10U, get_space_info_response.size); // Starting up from scratch shouldn't destroy the space either. storage::SetSpaceWriteError(42, false); NvramManager nvram2; GetSpaceInfoResponse get_space_info_response_2; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response_2)); EXPECT_EQ(10U, get_space_info_response_2.size); } TEST_F(NvramManagerTest, DisableCreate_Success) { NvramManager nvram; // Issue a successful disable create request. DisableCreateRequest disable_create_request; DisableCreateResponse disable_create_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DisableCreate(disable_create_request, &disable_create_response)); // Make sure space creation request fail afterwards. CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 32; ASSERT_TRUE(create_space_request.controls.Resize(1)); create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram.CreateSpace(create_space_request, &create_space_response)); // Redundant requests to disable creation are OK. EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DisableCreate(disable_create_request, &disable_create_response)); // Space creation should remain disabled even after a reboot. NvramManager nvram2; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram2.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, DisableCreate_WriteError) { // Make header writes fail. storage::SetHeaderWriteError(true); NvramManager nvram; // The disable request should fail. DisableCreateRequest disable_create_request; DisableCreateResponse disable_create_response; EXPECT_EQ( NV_RESULT_INTERNAL_ERROR, nvram.DisableCreate(disable_create_request, &disable_create_response)); // We should still be able to create spaces after clearing the error. storage::SetHeaderWriteError(false); CreateSpaceRequest create_space_request; create_space_request.index = 1; create_space_request.size = 32; ASSERT_TRUE(create_space_request.controls.Resize(1)); create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK; CreateSpaceResponse create_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.CreateSpace(create_space_request, &create_space_response)); } TEST_F(NvramManagerTest, WriteSpace_SpaceAbsent) { NvramManager nvram; // Attempt to write a non-existing space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("0123456789", 10)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.WriteSpace(write_space_request, &write_space_response)); } TEST_F(NvramManagerTest, WriteSpace_Success) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Write the space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("0123456789", 10)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WriteSpace(write_space_request, &write_space_response)); // Read back the space and compare contents. ReadAndCompareSpaceData(&nvram, 17, "0123456789", 10); // The data should persist even after a reboot. NvramManager nvram2; ReadAndCompareSpaceData(&nvram2, 17, "0123456789", 10); } TEST_F(NvramManagerTest, WriteSpace_ExcessData) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Write the space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("0123456789abcdef", 16)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.WriteSpace(write_space_request, &write_space_response)); } TEST_F(NvramManagerTest, WriteSpace_ShortData) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); memset(space.contents.data(), 'X', space.contents.size()); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Write the space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("01234", 5)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WriteSpace(write_space_request, &write_space_response)); // Read back the space data and verify that the missing content bytes have // been set to 0. const uint8_t kExpectedContents[] = {'0', '1', '2', '3', '4', 0, 0, 0, 0, 0}; ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 10); } TEST_F(NvramManagerTest, WriteSpace_WriteExtend) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_WRITE_EXTEND); ASSERT_TRUE(space.contents.Resize(32)); memset(space.contents.data(), 0, space.contents.size()); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Write the space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("data", 4)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WriteSpace(write_space_request, &write_space_response)); // Read back the space data and verify the hash. const uint8_t kExpectedContents[] = { 0xee, 0x84, 0x52, 0x88, 0xbb, 0x60, 0x7e, 0x02, 0xfd, 0xfb, 0x31, 0x95, 0x3a, 0x77, 0x23, 0xcf, 0x67, 0xea, 0x6e, 0x2d, 0xd7, 0xdb, 0x8c, 0xb4, 0xe4, 0xd2, 0xfd, 0xb4, 0x76, 0x7a, 0x67, 0x89, }; ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 32); } TEST_F(NvramManagerTest, WriteSpace_WriteExtendShortSpace) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_WRITE_EXTEND); ASSERT_TRUE(space.contents.Resize(16)); memset(space.contents.data(), 0, space.contents.size()); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Write the space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("data", 4)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WriteSpace(write_space_request, &write_space_response)); // Read back the space data and verify the truncated hash. const uint8_t kExpectedContents[] = { 0x24, 0x2a, 0xbb, 0x36, 0x10, 0x37, 0x92, 0x3f, 0x7d, 0x7d, 0x92, 0x3a, 0x16, 0x65, 0xd2, 0xa2, }; ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 16); } TEST_F(NvramManagerTest, WriteSpace_WriteExtendLongSpace) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_WRITE_EXTEND); ASSERT_TRUE(space.contents.Resize(33)); memset(space.contents.data(), 'X', space.contents.size()); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Write the space. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("data", 4)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WriteSpace(write_space_request, &write_space_response)); // Read back the space data and verify the hash and trailing 0 bytes. const uint8_t kExpectedContents[] = { 0x99, 0xb8, 0x5f, 0xd0, 0xf7, 0x9b, 0x17, 0x2e, 0x0e, 0x58, 0x3d, 0x3c, 0x9a, 0x29, 0xa3, 0xaf, 0x0a, 0x4c, 0x68, 0x97, 0x72, 0x8c, 0x0c, 0xa4, 0x37, 0xad, 0x39, 0xf3, 0x8c, 0x6e, 0x64, 0xd7, 0x00, }; ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 33); } TEST_F(NvramManagerTest, WriteSpace_AuthorizationFailure) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_WRITE_AUTHORIZATION); ASSERT_TRUE(space.contents.Resize(10)); const char kAuthorizationValue[] = "secret"; ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue, sizeof(kAuthorizationValue))); ASSERT_TRUE(space.contents.Assign("0123456789", 10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Attempt a write with the wrong authorization value. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("data", 4)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_ACCESS_DENIED, nvram.WriteSpace(write_space_request, &write_space_response)); // The previous data should remain effective. ReadAndCompareSpaceData(&nvram, 17, "0123456789", 10); } TEST_F(NvramManagerTest, WriteSpace_WriteError) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Assign("0123456789", 10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; storage::SetSpaceWriteError(17, true); // Attempt a write, which should fail. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("data", 4)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.WriteSpace(write_space_request, &write_space_response)); // The previous data should remain effective. ReadAndCompareSpaceData(&nvram, 17, "0123456789", 10); } TEST_F(NvramManagerTest, ReadSpace_SpaceAbsent) { NvramManager nvram; // Attempt a read from a space that doesn't exist. ReadSpaceRequest read_space_request; read_space_request.index = 17; ReadSpaceResponse read_space_response; EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.ReadSpace(read_space_request, &read_space_response)); } TEST_F(NvramManagerTest, ReadSpace_AuthorizationFailure) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_READ_AUTHORIZATION); ASSERT_TRUE(space.contents.Resize(10)); const char kAuthorizationValue[] = "secret"; ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue, sizeof(kAuthorizationValue))); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Attempt a read from the space. ReadSpaceRequest read_space_request; read_space_request.index = 17; ReadSpaceResponse read_space_response; EXPECT_EQ(NV_RESULT_ACCESS_DENIED, nvram.ReadSpace(read_space_request, &read_space_response)); EXPECT_EQ(0U, read_space_response.buffer.size()); } TEST_F(NvramManagerTest, LockSpaceWrite_SpaceAbsent) { NvramManager nvram; // Attempt to lock a space that doesn't exist. LockSpaceWriteRequest lock_space_write_request; lock_space_write_request.index = 17; LockSpaceWriteResponse lock_space_write_response; EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.LockSpaceWrite(lock_space_write_request, &lock_space_write_response)); } TEST_F(NvramManagerTest, LockSpaceWrite_AuthorizationFailure) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK) | (1 << NV_CONTROL_WRITE_AUTHORIZATION); ASSERT_TRUE(space.contents.Resize(10)); const char kAuthorizationValue[] = "secret"; ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue, sizeof(kAuthorizationValue))); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Attempt to lock a space without valid authentication. LockSpaceWriteRequest lock_space_write_request; lock_space_write_request.index = 17; LockSpaceWriteResponse lock_space_write_response; EXPECT_EQ(NV_RESULT_ACCESS_DENIED, nvram.LockSpaceWrite(lock_space_write_request, &lock_space_write_response)); } TEST_F(NvramManagerTest, LockSpaceWrite_SuccessPersistent) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK); ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Lock the space. LockSpaceWriteRequest lock_space_write_request; lock_space_write_request.index = 17; LockSpaceWriteResponse lock_space_write_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.LockSpaceWrite(lock_space_write_request, &lock_space_write_response)); // Writing should fail now. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("data", 4)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram.WriteSpace(write_space_request, &write_space_response)); // The lock should be persistent, so writing should fail after reboot. NvramManager nvram2; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram2.WriteSpace(write_space_request, &write_space_response)); } TEST_F(NvramManagerTest, LockSpaceWrite_SuccessBoot) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_BOOT_WRITE_LOCK); ASSERT_TRUE(space.contents.Assign("01234567890", 10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Lock the space. LockSpaceWriteRequest lock_space_write_request; lock_space_write_request.index = 17; LockSpaceWriteResponse lock_space_write_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.LockSpaceWrite(lock_space_write_request, &lock_space_write_response)); // Writing should fail now. WriteSpaceRequest write_space_request; write_space_request.index = 17; ASSERT_TRUE(write_space_request.buffer.Assign("newcontent", 10)); WriteSpaceResponse write_space_response; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram.WriteSpace(write_space_request, &write_space_response)); // We configured a per-boot lock, so writing should succeed after reboot. NvramManager nvram2; EXPECT_EQ(NV_RESULT_SUCCESS, nvram2.WriteSpace(write_space_request, &write_space_response)); ReadAndCompareSpaceData(&nvram2, 17, "newcontent", 10); } TEST_F(NvramManagerTest, LockSpaceWrite_NotLockable) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Attempt to lock a space without valid authentication. LockSpaceWriteRequest lock_space_write_request; lock_space_write_request.index = 17; LockSpaceWriteResponse lock_space_write_response; EXPECT_EQ(NV_RESULT_INVALID_PARAMETER, nvram.LockSpaceWrite(lock_space_write_request, &lock_space_write_response)); } TEST_F(NvramManagerTest, LockSpaceRead_SpaceAbsent) { NvramManager nvram; // Attempt to lock a non-existing space. LockSpaceReadRequest lock_space_read_request; lock_space_read_request.index = 17; LockSpaceReadResponse lock_space_read_response; EXPECT_EQ( NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response)); } TEST_F(NvramManagerTest, LockSpaceRead_AuthorizationFailure) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_BOOT_READ_LOCK) | (1 << NV_CONTROL_READ_AUTHORIZATION); ASSERT_TRUE(space.contents.Resize(10)); const char kAuthorizationValue[] = "secret"; ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue, sizeof(kAuthorizationValue))); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Attempt to lock a space without valid authorization. LockSpaceReadRequest lock_space_read_request; lock_space_read_request.index = 17; LockSpaceReadResponse lock_space_read_response; EXPECT_EQ( NV_RESULT_ACCESS_DENIED, nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response)); } TEST_F(NvramManagerTest, LockSpaceRead_Success) { // Set up an NVRAM space. NvramSpace space; space.controls = (1 << NV_CONTROL_BOOT_READ_LOCK); ASSERT_TRUE(space.contents.Assign("0123456789", 10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Lock the space. LockSpaceReadRequest lock_space_read_request; lock_space_read_request.index = 17; LockSpaceReadResponse lock_space_read_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response)); // Read requests should fail now. ReadSpaceRequest read_space_request; read_space_request.index = 17; ReadSpaceResponse read_space_response; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram.ReadSpace(read_space_request, &read_space_response)); EXPECT_EQ(0U, read_space_response.buffer.size()); // This is a non-persistent lock, so reads should work again after a reboot. NvramManager nvram2; ReadAndCompareSpaceData(&nvram2, 17, "0123456789", 10); } TEST_F(NvramManagerTest, LockSpaceRead_NotLockable) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Attempt to lock a space without valid authorization. LockSpaceReadRequest lock_space_read_request; lock_space_read_request.index = 17; LockSpaceReadResponse lock_space_read_response; EXPECT_EQ( NV_RESULT_INVALID_PARAMETER, nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response)); } TEST_F(NvramManagerTest, WipeStorage_Success) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); // Check that the space is visible. NvramManager nvram; GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 17; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10U, get_space_info_response.size); // Request a wipe. WipeStorageRequest wipe_storage_request; WipeStorageResponse wipe_storage_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WipeStorage(wipe_storage_request, &wipe_storage_response)); // The space should no longer be declared. GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetInfo(get_info_request, &get_info_response)); EXPECT_EQ(0U, get_info_response.space_list.size()); // Accessing the space should fail. EXPECT_EQ( NV_RESULT_SPACE_DOES_NOT_EXIST, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); } TEST_F(NvramManagerTest, WipeStorage_Abort) { // Set up two pre-existing spaces and a matching header. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space)); ASSERT_TRUE(space.contents.Resize(20)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(2, space)); NvramHeader header; header.version = NvramHeader::kVersion; ASSERT_TRUE(header.allocated_indices.Resize(2)); header.allocated_indices[0] = 1; header.allocated_indices[1] = 2; ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header)); // Check that the spaces are visible. NvramManager nvram; GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetInfo(get_info_request, &get_info_response)); EXPECT_EQ(2U, get_info_response.space_list.size()); int space_mask = 0; for (size_t i = 0; i < get_info_response.space_list.size(); ++i) { space_mask |= (1 << get_info_response.space_list[i]); } EXPECT_EQ(0x6, space_mask); // Set things up so the deletion request for the second space fails. storage::SetSpaceWriteError(2, true); // The wipe request should fail now. WipeStorageRequest wipe_storage_request; WipeStorageResponse wipe_storage_response; EXPECT_EQ(NV_RESULT_INTERNAL_ERROR, nvram.WipeStorage(wipe_storage_request, &wipe_storage_response)); // New wipe attempt with a fresh instance after clearing the error. storage::SetSpaceWriteError(2, false); NvramManager nvram2; EXPECT_EQ(NV_RESULT_SUCCESS, nvram2.WipeStorage(wipe_storage_request, &wipe_storage_response)); // No spaces should remain. EXPECT_EQ(NV_RESULT_SUCCESS, nvram2.GetInfo(get_info_request, &get_info_response)); EXPECT_EQ(0U, get_info_response.space_list.size()); } TEST_F(NvramManagerTest, WipeStorage_Disable) { // Set up an NVRAM space. NvramSpace space; ASSERT_TRUE(space.contents.Resize(10)); ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space)); SetupHeader(NvramHeader::kVersion, 17); NvramManager nvram; // Disable wiping. DisableWipeRequest disable_wipe_request; DisableWipeResponse disable_wipe_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DisableWipe(disable_wipe_request, &disable_wipe_response)); // A wipe request should fail. WipeStorageRequest wipe_storage_request; WipeStorageResponse wipe_storage_response; EXPECT_EQ(NV_RESULT_OPERATION_DISABLED, nvram.WipeStorage(wipe_storage_request, &wipe_storage_response)); // The space should remain declared. GetInfoRequest get_info_request; GetInfoResponse get_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetInfo(get_info_request, &get_info_response)); ASSERT_EQ(1U, get_info_response.space_list.size()); EXPECT_EQ(17U, get_info_response.space_list[0]); // The space data should remain present. GetSpaceInfoRequest get_space_info_request; get_space_info_request.index = 17; GetSpaceInfoResponse get_space_info_response; EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response)); EXPECT_EQ(10U, get_space_info_response.size); } } // namespace } // namespace nvram