| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <cbfs.h> |
| #include <cbfs_glue.h> |
| #include <string.h> |
| #include <mocks/cbfs_util.h> |
| #include <tests/test.h> |
| |
| #include "../libcbfs/cbfs.c" |
| |
| /* Mocks */ |
| |
| unsigned long virtual_offset = 0; |
| struct sysinfo_t lib_sysinfo; |
| |
| size_t vb2_digest_size(enum vb2_hash_algorithm hash_alg) |
| { |
| if (hash_alg != VB2_HASH_SHA256) { |
| fail_msg("Unsupported hash algorithm: %d\n", hash_alg); |
| return 0; |
| } |
| |
| return VB2_SHA256_DIGEST_SIZE; |
| } |
| |
| vb2_error_t vb2_hash_verify(bool allow_hwcrypto, const void *buf, uint32_t size, |
| const struct vb2_hash *hash) |
| { |
| assert_true(allow_hwcrypto); |
| check_expected_ptr(buf); |
| check_expected(size); |
| |
| assert_int_equal(hash->algo, VB2_HASH_SHA256); |
| |
| if (!memcmp(hash->sha256, good_hash, sizeof(good_hash))) |
| return VB2_SUCCESS; |
| |
| if (!memcmp(hash->sha256, bad_hash, sizeof(bad_hash))) |
| return VB2_ERROR_SHA_MISMATCH; |
| |
| fail_msg("%s called with bad hash", __func__); |
| return VB2_ERROR_SHA_MISMATCH; |
| } |
| |
| bool vb2api_hwcrypto_allowed(struct vb2_context *ctx) |
| { |
| return true; |
| } |
| |
| struct vb2_context *vboot_get_context(void) |
| { |
| return NULL; |
| } |
| |
| unsigned long ulzman(const unsigned char *src, unsigned long srcn, unsigned char *dst, |
| unsigned long dstn) |
| { |
| size_t copy_size = MIN(srcn, dstn); |
| function_called(); |
| memcpy(dst, src, copy_size); |
| return copy_size; |
| } |
| |
| size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn) |
| { |
| size_t copy_size = MIN(srcn, dstn); |
| function_called(); |
| memcpy(dst, src, copy_size); |
| return copy_size; |
| } |
| |
| enum cb_err cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, |
| union cbfs_mdata *mdata_out, size_t *data_offset_out) |
| { |
| return CB_CBFS_CACHE_FULL; |
| } |
| |
| enum cb_err cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out, |
| size_t *data_offset_out, struct vb2_hash *metadata_hash) |
| { |
| assert_non_null(dev); |
| check_expected(name); |
| |
| enum cb_err ret = mock_type(enum cb_err); |
| if (ret != CB_SUCCESS) |
| return ret; |
| |
| memcpy(mdata_out, mock_ptr_type(const union cbfs_mdata *), sizeof(union cbfs_mdata)); |
| *data_offset_out = mock_type(size_t); |
| return CB_SUCCESS; |
| } |
| |
| static void expect_cbfs_lookup(const char *name, enum cb_err err, const union cbfs_mdata *mdata, |
| size_t data_offset_out) |
| { |
| expect_string(cbfs_lookup, name, name); |
| will_return(cbfs_lookup, err); |
| |
| if (err == CB_SUCCESS) { |
| will_return(cbfs_lookup, mdata); |
| will_return(cbfs_lookup, data_offset_out); |
| } |
| } |
| |
| const void *cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check) |
| { |
| return mock_ptr_type(void *); |
| } |
| |
| enum cb_err fmap_locate_area(const char *name, size_t *offset, size_t *size) |
| { |
| *offset = 0; |
| *size = 0; |
| return CB_SUCCESS; |
| } |
| |
| ssize_t boot_device_read(void *buf, size_t offset, size_t size) |
| { |
| /* Offset should be based on an address from lib_sysinfo.cbfs_offset */ |
| memcpy(buf, (void *)offset, size); |
| |
| return size; |
| } |
| |
| const struct vb2_hash *cbfs_file_hash(const union cbfs_mdata *mdata) |
| { |
| return mock_ptr_type(const struct vb2_hash *); |
| } |
| |
| /* Utils */ |
| |
| static void clear_cbfs_boot_devices(void) |
| { |
| lib_sysinfo.cbfs_ro_mcache_offset = 0; |
| lib_sysinfo.cbfs_ro_mcache_size = 0; |
| lib_sysinfo.cbfs_offset = 0; |
| lib_sysinfo.cbfs_size = 0; |
| lib_sysinfo.cbfs_rw_mcache_offset = 0; |
| lib_sysinfo.cbfs_rw_mcache_size = 0; |
| memset((void *)cbfs_get_boot_device(true), 0, sizeof(struct cbfs_boot_device)); |
| memset((void *)cbfs_get_boot_device(false), 0, sizeof(struct cbfs_boot_device)); |
| } |
| |
| void set_cbfs(uint64_t offset, size_t size) |
| { |
| clear_cbfs_boot_devices(); |
| lib_sysinfo.cbfs_offset = offset; |
| lib_sysinfo.cbfs_size = size; |
| } |
| |
| /* Tests */ |
| |
| static int setup_test_cbfs(void **state) |
| { |
| clear_cbfs_boot_devices(); |
| return 0; |
| } |
| |
| static void test_cbfs_map_no_hash(void **state) |
| { |
| void *mapping = NULL; |
| size_t size = 0; |
| |
| set_cbfs((uint64_t)&file_no_hash, sizeof(file_no_hash)); |
| |
| expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS, |
| (const union cbfs_mdata *)&file_no_hash, |
| be32toh(file_no_hash.header.offset)); |
| will_return(cbfs_find_attr, NULL); |
| |
| if (CONFIG(LP_CBFS_VERIFICATION)) { |
| /* File with no hash. No hash causes hash mismatch by default, |
| so mapping will not be completed successfully. */ |
| will_return(cbfs_file_hash, NULL); |
| mapping = cbfs_map(TEST_DATA_1_FILENAME, NULL); |
| assert_null(mapping); |
| } else { |
| mapping = cbfs_map(TEST_DATA_1_FILENAME, &size); |
| assert_non_null(mapping); |
| assert_int_equal(TEST_DATA_1_SIZE, size); |
| assert_memory_equal(test_data_1, mapping, size); |
| cbfs_unmap(mapping); |
| } |
| } |
| |
| static void test_cbfs_map_valid_hash_impl(void **state, bool lz4_compressed) |
| { |
| void *mapping = NULL; |
| size_t size = 0; |
| struct vb2_hash hash = { |
| .algo = VB2_HASH_SHA256, |
| }; |
| memcpy(&hash.sha256, good_hash, VB2_SHA256_DIGEST_SIZE); |
| |
| set_cbfs((uint64_t)&file_valid_hash, sizeof(file_valid_hash)); |
| |
| expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS, |
| (const union cbfs_mdata *)&file_valid_hash, |
| be32toh(file_valid_hash.header.offset)); |
| |
| if (lz4_compressed) { |
| struct cbfs_file_attr_compression cattr = { |
| .compression = htobe32(CBFS_COMPRESS_LZ4), |
| .decompressed_size = htobe32(TEST_DATA_1_SIZE), |
| }; |
| will_return(cbfs_find_attr, &cattr); |
| expect_function_call(ulz4fn); |
| } else { |
| will_return(cbfs_find_attr, NULL); |
| } |
| |
| if (CONFIG(LP_CBFS_VERIFICATION)) { |
| will_return(cbfs_file_hash, &hash); |
| expect_memory(vb2_hash_verify, buf, |
| &file_valid_hash.attrs_and_data[HASH_ATTR_SIZE], HASH_ATTR_SIZE); |
| expect_value(vb2_hash_verify, size, TEST_DATA_1_SIZE); |
| mapping = cbfs_map(TEST_DATA_1_FILENAME, &size); |
| assert_non_null(mapping); |
| assert_int_equal(TEST_DATA_1_SIZE, size); |
| assert_memory_equal(mapping, &file_valid_hash.attrs_and_data[HASH_ATTR_SIZE], |
| size); |
| } else { |
| mapping = cbfs_map(TEST_DATA_1_FILENAME, &size); |
| assert_non_null(mapping); |
| assert_int_equal(TEST_DATA_1_SIZE, size); |
| assert_memory_equal(test_data_1, mapping, size); |
| cbfs_unmap(mapping); |
| } |
| } |
| |
| static void test_cbfs_map_valid_hash(void **state) |
| { |
| test_cbfs_map_valid_hash_impl(state, false); |
| } |
| |
| static void test_cbfs_map_valid_hash_with_lz4(void **state) |
| { |
| test_cbfs_map_valid_hash_impl(state, true); |
| } |
| |
| static void test_cbfs_map_invalid_hash(void **state) |
| { |
| void *mapping = NULL; |
| size_t size = 0; |
| struct vb2_hash hash = { |
| .algo = VB2_HASH_SHA256, |
| }; |
| memcpy(&hash.sha256, bad_hash, VB2_SHA256_DIGEST_SIZE); |
| |
| set_cbfs((uint64_t)&file_broken_hash, sizeof(file_broken_hash)); |
| |
| expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS, |
| (const union cbfs_mdata *)&file_broken_hash, |
| be32toh(file_broken_hash.header.offset)); |
| will_return(cbfs_find_attr, NULL); |
| |
| if (CONFIG(LP_CBFS_VERIFICATION)) { |
| will_return(cbfs_file_hash, &hash); |
| expect_memory(vb2_hash_verify, buf, |
| &file_broken_hash.attrs_and_data[HASH_ATTR_SIZE], HASH_ATTR_SIZE); |
| expect_value(vb2_hash_verify, size, TEST_DATA_1_SIZE); |
| mapping = cbfs_map(TEST_DATA_1_FILENAME, NULL); |
| assert_null(mapping); |
| } else { |
| mapping = cbfs_map(TEST_DATA_1_FILENAME, &size); |
| assert_non_null(mapping); |
| assert_int_equal(TEST_DATA_1_SIZE, size); |
| assert_memory_equal(test_data_1, mapping, size); |
| cbfs_unmap(mapping); |
| } |
| } |
| |
| int main(void) |
| { |
| const struct CMUnitTest tests[] = { |
| cmocka_unit_test_setup(test_cbfs_map_no_hash, setup_test_cbfs), |
| cmocka_unit_test_setup(test_cbfs_map_valid_hash, setup_test_cbfs), |
| cmocka_unit_test_setup(test_cbfs_map_valid_hash_with_lz4, setup_test_cbfs), |
| cmocka_unit_test_setup(test_cbfs_map_invalid_hash, setup_test_cbfs), |
| }; |
| |
| return lp_run_group_tests(tests, NULL, NULL); |
| } |