From 044c8b32f0b552899f6769d72a073993c16d40ff Mon Sep 17 00:00:00 2001 From: shchmue Date: Mon, 7 Dec 2020 19:08:08 -0700 Subject: [PATCH] nx_savedata: More fail paths, ensure alignment --- .../nx_savedata/allocation_table_storage.c | 13 +++++++-- .../nx_savedata/allocation_table_storage.h | 2 +- ...rarchical_integrity_verification_storage.c | 4 +-- .../hierarchical_save_file_table.c | 4 +-- .../integrity_verification_storage.c | 13 +++++---- bdk/libs/nx_savedata/save.c | 27 ++++++++++--------- bdk/libs/nx_savedata/save_data_file.c | 2 +- .../nx_savedata/save_data_file_system_core.c | 18 +++++++++---- .../nx_savedata/save_data_file_system_core.h | 2 +- 9 files changed, 51 insertions(+), 34 deletions(-) diff --git a/bdk/libs/nx_savedata/allocation_table_storage.c b/bdk/libs/nx_savedata/allocation_table_storage.c index 43cea04..97b2dfb 100644 --- a/bdk/libs/nx_savedata/allocation_table_storage.c +++ b/bdk/libs/nx_savedata/allocation_table_storage.c @@ -38,12 +38,21 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include -void save_allocation_table_storage_init(allocation_table_storage_ctx_t *ctx, substorage *data, allocation_table_ctx_t *table, uint32_t block_size, uint32_t initial_block) { +bool save_allocation_table_storage_init(allocation_table_storage_ctx_t *ctx, substorage *data, allocation_table_ctx_t *table, uint32_t block_size, uint32_t initial_block) { ctx->base_storage = data; ctx->block_size = block_size; ctx->fat = table; ctx->initial_block = initial_block; - ctx->_length = initial_block == 0xFFFFFFFF ? 0 : save_allocation_table_get_list_length(table, initial_block) * block_size; + ctx->_length = 0; + if (initial_block != 0xFFFFFFFF) { + uint32_t list_length = save_allocation_table_get_list_length(table, initial_block); + if (list_length == 0) { + EPRINTF("Allocation table storage init failed!"); + return false; + }; + ctx->_length = list_length * block_size; + } + return true; } uint32_t save_allocation_table_storage_read(allocation_table_storage_ctx_t *ctx, void *buffer, uint64_t offset, uint64_t count) { diff --git a/bdk/libs/nx_savedata/allocation_table_storage.h b/bdk/libs/nx_savedata/allocation_table_storage.h index 86f55cf..6e8d30c 100644 --- a/bdk/libs/nx_savedata/allocation_table_storage.h +++ b/bdk/libs/nx_savedata/allocation_table_storage.h @@ -52,7 +52,7 @@ static ALWAYS_INLINE void save_allocation_table_storage_get_size(allocation_tabl *out_size = ctx->_length; } -void save_allocation_table_storage_init(allocation_table_storage_ctx_t *ctx, substorage *data, allocation_table_ctx_t *table, uint32_t block_size, uint32_t initial_block); +bool save_allocation_table_storage_init(allocation_table_storage_ctx_t *ctx, substorage *data, allocation_table_ctx_t *table, uint32_t block_size, uint32_t initial_block); uint32_t save_allocation_table_storage_read(allocation_table_storage_ctx_t *ctx, void *buffer, uint64_t offset, uint64_t count); uint32_t save_allocation_table_storage_write(allocation_table_storage_ctx_t *ctx, const void *buffer, uint64_t offset, uint64_t count); bool save_allocation_table_storage_set_size(allocation_table_storage_ctx_t *ctx, uint64_t size); diff --git a/bdk/libs/nx_savedata/hierarchical_integrity_verification_storage.c b/bdk/libs/nx_savedata/hierarchical_integrity_verification_storage.c index 4d8153b..d244571 100644 --- a/bdk/libs/nx_savedata/hierarchical_integrity_verification_storage.c +++ b/bdk/libs/nx_savedata/hierarchical_integrity_verification_storage.c @@ -135,7 +135,7 @@ validity_t save_hierarchical_integrity_verification_storage_validate(hierarchica validity_t result = VALIDITY_VALID; integrity_verification_storage_ctx_t *storage = &ctx->integrity_storages[3]; - uint64_t block_size = storage->base_storage.sector_size; + uint32_t block_size = storage->base_storage.sector_size; uint32_t block_count = (uint32_t)(DIV_ROUND_UP(ctx->length, block_size)); uint8_t *buffer = malloc(block_size); @@ -143,7 +143,7 @@ validity_t save_hierarchical_integrity_verification_storage_validate(hierarchica for (unsigned int i = 0; i < block_count; i++) { if (ctx->level_validities[3][i] == VALIDITY_UNCHECKED) { uint64_t storage_size = storage->base_storage.length; - uint32_t to_read = MIN((uint32_t)(storage_size - block_size * i), (uint32_t)block_size); + uint32_t to_read = MIN((uint32_t)(storage_size - block_size * i), block_size); substorage_read(&ctx->data_level->base_storage, buffer, block_size * i, to_read); } if (ctx->level_validities[3][i] == VALIDITY_INVALID) { diff --git a/bdk/libs/nx_savedata/hierarchical_save_file_table.c b/bdk/libs/nx_savedata/hierarchical_save_file_table.c index 0ae9126..0aae473 100644 --- a/bdk/libs/nx_savedata/hierarchical_save_file_table.c +++ b/bdk/libs/nx_savedata/hierarchical_save_file_table.c @@ -207,7 +207,7 @@ void save_hierarchical_file_table_unlink_file_from_parent(hierarchical_save_file return; } prev_index = cur_index; - memcpy(&prev_entry, &cur_entry, sizeof(prev_entry)); + memcpy(&prev_entry, &cur_entry, sizeof(save_table_entry_t)); cur_index = prev_entry.next_sibling; } } @@ -236,7 +236,7 @@ void save_hierarchical_file_table_unlink_directory_from_parent(hierarchical_save return; } prev_index = cur_index; - memcpy(&prev_entry, &cur_entry, sizeof(prev_entry)); + memcpy(&prev_entry, &cur_entry, sizeof(save_table_entry_t)); cur_index = prev_entry.next_sibling; } } diff --git a/bdk/libs/nx_savedata/integrity_verification_storage.c b/bdk/libs/nx_savedata/integrity_verification_storage.c index fc76501..3e5bba7 100644 --- a/bdk/libs/nx_savedata/integrity_verification_storage.c +++ b/bdk/libs/nx_savedata/integrity_verification_storage.c @@ -52,7 +52,7 @@ void save_ivfc_storage_init(integrity_verification_storage_ctx_t *ctx, integrity /* buffer must have size count + 0x20 for salt to by copied in at offset 0. */ static ALWAYS_INLINE void save_ivfc_storage_do_hash(integrity_verification_storage_ctx_t *ctx, uint8_t *out_hash, void *buffer, uint64_t count) { memcpy(buffer, ctx->salt, sizeof(ctx->salt)); - se_calc_sha256(out_hash, buffer, count + sizeof(ctx->salt)); + se_calc_sha256_oneshot(out_hash, buffer, count + sizeof(ctx->salt)); out_hash[0x1F] |= 0x80; } @@ -81,7 +81,7 @@ bool save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf return false; } - uint8_t hash_buffer[0x20] = {0}; + uint8_t hash_buffer[0x20] __attribute__((aligned(4))) = {0}; uint64_t hash_pos = block_index * sizeof(hash_buffer); if (substorage_read(&ctx->hash_storage, hash_buffer, hash_pos, sizeof(hash_buffer)) != sizeof(hash_buffer)) @@ -99,17 +99,16 @@ bool save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf return false; } + memcpy(buffer, data_buffer + 0x20 + (offset % ctx->base_storage.sector_size), count); + if (ctx->integrity_check_level && ctx->block_validities[block_index] != VALIDITY_UNCHECKED) { - memcpy(buffer, data_buffer + 0x20 + (offset % ctx->base_storage.sector_size), count); free(data_buffer); return true; } - uint8_t hash[0x20] = {0}; + uint8_t hash[0x20] __attribute__((aligned(4))) = {0}; save_ivfc_storage_do_hash(ctx, hash, data_buffer, ctx->base_storage.sector_size); - memcpy(buffer, data_buffer + 0x20 + (offset % ctx->base_storage.sector_size), count); free(data_buffer); - if (memcmp(hash_buffer, hash, sizeof(hash_buffer)) == 0) { ctx->block_validities[block_index] = VALIDITY_VALID; } else { @@ -127,7 +126,7 @@ bool save_ivfc_storage_write(integrity_verification_storage_ctx_t *ctx, const vo uint64_t block_index = offset / ctx->base_storage.sector_size; uint64_t hash_pos = block_index * 0x20; - uint8_t hash[0x20] = {0}; + uint8_t hash[0x20] __attribute__((aligned(4))) = {0}; uint8_t *data_buffer = calloc(1, ctx->base_storage.sector_size + 0x20); if (count < ctx->base_storage.sector_size) { if (substorage_read(&ctx->base_storage.base_storage, data_buffer + 0x20, offset - (offset % ctx->base_storage.sector_size), ctx->base_storage.sector_size) != ctx->base_storage.sector_size) { diff --git a/bdk/libs/nx_savedata/save.c b/bdk/libs/nx_savedata/save.c index 7669e10..979c948 100644 --- a/bdk/libs/nx_savedata/save.c +++ b/bdk/libs/nx_savedata/save.c @@ -98,13 +98,13 @@ static bool save_process_header(save_ctx_t *ctx) { ctx->data_ivfc_master = (uint8_t *)&ctx->header + ctx->header.layout.ivfc_master_hash_offset_a; ctx->fat_ivfc_master = (uint8_t *)&ctx->header + ctx->header.layout.fat_ivfc_master_hash_a; - uint8_t hash[0x20]; + uint8_t hash[0x20] __attribute__((aligned(4))); uint32_t hashed_data_offset = sizeof(ctx->header.layout) + sizeof(ctx->header.cmac) + sizeof(ctx->header._0x10); uint32_t hashed_data_size = sizeof(ctx->header) - hashed_data_offset; - se_calc_sha256(hash, (uint8_t *)&ctx->header + hashed_data_offset, hashed_data_size); - ctx->header_hash_validity = memcmp(hash, ctx->header.layout.hash, 0x20) == 0 ? VALIDITY_VALID : VALIDITY_INVALID; + se_calc_sha256_oneshot(hash, (uint8_t *)&ctx->header + hashed_data_offset, hashed_data_size); + ctx->header_hash_validity = memcmp(hash, ctx->header.layout.hash, sizeof(hash)) == 0 ? VALIDITY_VALID : VALIDITY_INVALID; - unsigned char cmac[0x10] = {}; + uint8_t cmac[0x10] __attribute__((aligned(4))); se_aes_key_set(10, ctx->save_mac_key, 0x10); se_aes_cmac(10, cmac, 0x10, &ctx->header.layout, sizeof(ctx->header.layout)); if (memcmp(cmac, &ctx->header.cmac, 0x10) == 0) { @@ -126,14 +126,14 @@ bool save_process(save_ctx_t *ctx) { substorage_init(&ctx->base_storage, &file_storage_vt, ctx->file, 0, f_size(ctx->file)); /* Try to parse Header A. */ if (substorage_read(&ctx->base_storage, &ctx->header, 0, sizeof(ctx->header)) != sizeof(ctx->header)) { - EPRINTF("Failed to read save header!\n"); + EPRINTF("Failed to read save header A!\n"); return false; } if (!save_process_header(ctx) || (ctx->header_hash_validity == VALIDITY_INVALID)) { /* Try to parse Header B. */ if (substorage_read(&ctx->base_storage, &ctx->header, sizeof(ctx->header), sizeof(ctx->header)) != sizeof(ctx->header)) { - EPRINTF("Failed to read save header!\n"); + EPRINTF("Failed to read save header B!\n"); return false; } @@ -147,10 +147,13 @@ bool save_process(save_ctx_t *ctx) { ctx->data_remap_storage.header = &ctx->header.main_remap_header; ctx->meta_remap_storage.header = &ctx->header.meta_remap_header; + u32 data_remap_entry_size = sizeof(remap_entry_t) * ctx->data_remap_storage.header->map_entry_count; + u32 meta_remap_entry_size = sizeof(remap_entry_t) * ctx->meta_remap_storage.header->map_entry_count; + substorage_init(&ctx->data_remap_storage.base_storage, &file_storage_vt, ctx->file, ctx->header.layout.file_map_data_offset, ctx->header.layout.file_map_data_size); ctx->data_remap_storage.map_entries = calloc(1, sizeof(remap_entry_ctx_t) * ctx->data_remap_storage.header->map_entry_count); - uint8_t *remap_buffer = malloc(MAX(ctx->data_remap_storage.header->map_entry_count, ctx->meta_remap_storage.header->map_entry_count) * sizeof(remap_entry_t)); - if (substorage_read(&ctx->base_storage, remap_buffer, ctx->header.layout.file_map_entry_offset, sizeof(remap_entry_t) * ctx->data_remap_storage.header->map_entry_count) != sizeof(remap_entry_t) * ctx->data_remap_storage.header->map_entry_count) { + uint8_t *remap_buffer = malloc(MAX(data_remap_entry_size, meta_remap_entry_size)); + if (substorage_read(&ctx->base_storage, remap_buffer, ctx->header.layout.file_map_entry_offset, data_remap_entry_size) != data_remap_entry_size) { EPRINTF("Failed to read data remap table!"); free(remap_buffer); return false; @@ -177,7 +180,7 @@ bool save_process(save_ctx_t *ctx) { /* Initialize meta remap storage. */ substorage_init(&ctx->meta_remap_storage.base_storage, &hierarchical_duplex_storage_vt, &ctx->duplex_storage, 0, ctx->duplex_storage.data_layer->_length); ctx->meta_remap_storage.map_entries = calloc(1, sizeof(remap_entry_ctx_t) * ctx->meta_remap_storage.header->map_entry_count); - if (substorage_read(&ctx->base_storage, remap_buffer, ctx->header.layout.meta_map_entry_offset, sizeof(remap_entry_t) * ctx->meta_remap_storage.header->map_entry_count) != sizeof(remap_entry_t) * ctx->meta_remap_storage.header->map_entry_count) { + if (substorage_read(&ctx->base_storage, remap_buffer, ctx->header.layout.meta_map_entry_offset, meta_remap_entry_size) != meta_remap_entry_size) { EPRINTF("Failed to read meta remap table!"); free(remap_buffer); return false; @@ -225,9 +228,7 @@ bool save_process(save_ctx_t *ctx) { } /* Initialize core save filesystem. */ - save_data_file_system_core_init(&ctx->save_filesystem_core, &ctx->core_data_ivfc_storage.base_storage, ctx->fat_storage, &ctx->header.save_header); - - return true; + return save_data_file_system_core_init(&ctx->save_filesystem_core, &ctx->core_data_ivfc_storage.base_storage, ctx->fat_storage, &ctx->header.save_header); } void save_free_contexts(save_ctx_t *ctx) { @@ -292,7 +293,7 @@ bool save_commit(save_ctx_t *ctx) { uint32_t hashed_data_offset = sizeof(ctx->header.layout) + sizeof(ctx->header.cmac) + sizeof(ctx->header._0x10); uint32_t hashed_data_size = sizeof(ctx->header) - hashed_data_offset; uint8_t *header = (uint8_t *)&ctx->header; - se_calc_sha256(ctx->header.layout.hash, header + hashed_data_offset, hashed_data_size); + se_calc_sha256_oneshot(ctx->header.layout.hash, header + hashed_data_offset, hashed_data_size); se_aes_key_set(10, ctx->save_mac_key, 0x10); se_aes_cmac(10, ctx->header.cmac, 0x10, &ctx->header.layout, sizeof(ctx->header.layout)); diff --git a/bdk/libs/nx_savedata/save_data_file.c b/bdk/libs/nx_savedata/save_data_file.c index 0940052..edf85ba 100644 --- a/bdk/libs/nx_savedata/save_data_file.c +++ b/bdk/libs/nx_savedata/save_data_file.c @@ -38,7 +38,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. void save_data_file_init(save_data_file_ctx_t *ctx, allocation_table_storage_ctx_t *base_storage, const char *path, hierarchical_save_file_table_ctx_t *file_table, uint64_t size, open_mode_t mode) { ctx->mode = mode; - memcpy(&ctx->base_storage, base_storage, sizeof(ctx->base_storage)); + memcpy(&ctx->base_storage, base_storage, sizeof(allocation_table_storage_ctx_t)); ctx->path = path; ctx->file_table = file_table; ctx->size = size; diff --git a/bdk/libs/nx_savedata/save_data_file_system_core.c b/bdk/libs/nx_savedata/save_data_file_system_core.c index ccd3a2c..03275cb 100644 --- a/bdk/libs/nx_savedata/save_data_file_system_core.c +++ b/bdk/libs/nx_savedata/save_data_file_system_core.c @@ -41,21 +41,29 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -static ALWAYS_INLINE void save_data_file_system_core_open_fat_storage(save_data_file_system_core_ctx_t *ctx, allocation_table_storage_ctx_t *storage_ctx, uint32_t block_index) { - save_allocation_table_storage_init(storage_ctx, ctx->base_storage, &ctx->allocation_table, (uint32_t)ctx->header->block_size, block_index); +static ALWAYS_INLINE bool save_data_file_system_core_open_fat_storage(save_data_file_system_core_ctx_t *ctx, allocation_table_storage_ctx_t *storage_ctx, uint32_t block_index) { + return save_allocation_table_storage_init(storage_ctx, ctx->base_storage, &ctx->allocation_table, (uint32_t)ctx->header->block_size, block_index); } -void save_data_file_system_core_init(save_data_file_system_core_ctx_t *ctx, substorage *storage, void *allocation_table, save_fs_header_t *save_fs_header) { +bool save_data_file_system_core_init(save_data_file_system_core_ctx_t *ctx, substorage *storage, void *allocation_table, save_fs_header_t *save_fs_header) { save_allocation_table_init(&ctx->allocation_table, allocation_table, &save_fs_header->fat_header); ctx->header = save_fs_header; ctx->base_storage = storage; save_filesystem_list_ctx_t *dir_table = &ctx->file_table.directory_table; save_filesystem_list_ctx_t *file_table = &ctx->file_table.file_table; - save_data_file_system_core_open_fat_storage(ctx, &dir_table->storage, save_fs_header->fat_header.directory_table_block); - save_data_file_system_core_open_fat_storage(ctx, &file_table->storage, save_fs_header->fat_header.file_table_block); + if (!save_data_file_system_core_open_fat_storage(ctx, &dir_table->storage, save_fs_header->fat_header.directory_table_block)) { + EPRINTF("Failed to init dir table for fs core!"); + return false; + } + if (!save_data_file_system_core_open_fat_storage(ctx, &file_table->storage, save_fs_header->fat_header.file_table_block)) { + EPRINTF("Failed to init file table for fs core!"); + return false; + } save_fs_list_init(dir_table); save_fs_list_init(file_table); + + return true; } bool save_data_file_system_core_create_directory(save_data_file_system_core_ctx_t *ctx, const char *path) { diff --git a/bdk/libs/nx_savedata/save_data_file_system_core.h b/bdk/libs/nx_savedata/save_data_file_system_core.h index 14c4523..3c69cde 100644 --- a/bdk/libs/nx_savedata/save_data_file_system_core.h +++ b/bdk/libs/nx_savedata/save_data_file_system_core.h @@ -66,7 +66,7 @@ static ALWAYS_INLINE void save_data_file_system_core_get_total_space_size(save_d *out_total_space = ctx->header->block_size * ctx->header->block_count; } -void save_data_file_system_core_init(save_data_file_system_core_ctx_t *ctx, substorage *storage, void *allocation_table, save_fs_header_t *save_fs_header); +bool save_data_file_system_core_init(save_data_file_system_core_ctx_t *ctx, substorage *storage, void *allocation_table, save_fs_header_t *save_fs_header); bool save_data_file_system_core_create_directory(save_data_file_system_core_ctx_t *ctx, const char *path); bool save_data_file_system_core_create_file(save_data_file_system_core_ctx_t *ctx, const char *path, uint64_t size);