From ecaeddf3560c0abee90306b5eae6661fc5a413c7 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Thu, 12 Oct 2023 11:24:03 +0200 Subject: [PATCH] Fix Patch RomFS ctx init w/missing base NCA FS. This bug was introduced in c474435ea8a811bfec77f6b991313df0edf5c9d0. Other changes include: * nxdt_rw_poc, bktr: minor cosmetic code changes. * cnmt: add ContentMetaPlatform enum. --- code_templates/nxdt_rw_poc.c | 2 +- include/core/cnmt.h | 25 +++++++++++++++---------- include/core/nca_storage.h | 5 ++--- source/core/bktr.c | 4 ++-- source/core/cnmt.c | 6 ++++++ source/core/nca_storage.c | 28 +++++++++++++++++----------- source/core/romfs.c | 2 +- 7 files changed, 44 insertions(+), 28 deletions(-) diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index 18109de..7599ca3 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -934,7 +934,7 @@ int main(int argc, char *argv[]) } consoleClear(); - consolePrint(APP_TITLE " v" APP_VERSION " (" GIT_REV "). Built on " BUILD_TIMESTAMP ".\n"); + consolePrint(APP_TITLE " v" APP_VERSION " (" GIT_REV ").\nBuilt on " BUILD_TIMESTAMP ".\n"); consolePrint("______________________________\n\n"); if (cur_menu->parent) consolePrint("press b to go back\n"); if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices\n"); diff --git a/include/core/cnmt.h b/include/core/cnmt.h index 3a29b86..c3aa201 100644 --- a/include/core/cnmt.h +++ b/include/core/cnmt.h @@ -41,6 +41,11 @@ typedef enum { ContentMetaAttribute_Count = 3 ///< Total values supported by this enum. } ContentMetaAttribute; +typedef enum { + ContentMetaPlatform_Nx = 0, + ContentMetaPlatform_Count = 1 ///< Total values supported by this enum. +} ContentMetaPlatform; + typedef enum { ContentMetaInstallState_None = 0, ContentMetaInstallState_Committed = BIT(0), @@ -55,17 +60,17 @@ typedef enum { typedef struct { u64 title_id; Version version; - u8 content_meta_type; ///< NcmContentMetaType. - u8 reserved_1; - u16 extended_header_size; ///< Must match the size from the extended header struct for this content meta type (SystemUpdate, Application, Patch, AddOnContent, Delta). - u16 content_count; ///< Determines how many NcmPackagedContentInfo entries are available after the extended header. - u16 content_meta_count; ///< Determines how many NcmContentMetaInfo entries are available after the NcmPackagedContentInfo entries. Only used for SystemUpdate. - u8 content_meta_attribute; ///< ContentMetaAttribute. - u8 storage_id; ///< NcmStorageId. - u8 content_install_type; ///< NcmContentInstallType. - u8 install_state; ///< ContentMetaInstallState. + u8 content_meta_type; ///< NcmContentMetaType. + u8 content_meta_platform; ///< ContentMetaPlatform. + u16 extended_header_size; ///< Must match the size from the extended header struct for this content meta type (SystemUpdate, Application, Patch, AddOnContent, Delta). + u16 content_count; ///< Determines how many NcmPackagedContentInfo entries are available after the extended header. + u16 content_meta_count; ///< Determines how many NcmContentMetaInfo entries are available after the NcmPackagedContentInfo entries. Only used for SystemUpdate. + u8 content_meta_attribute; ///< ContentMetaAttribute. + u8 storage_id; ///< NcmStorageId. + u8 content_install_type; ///< NcmContentInstallType. + u8 install_state; ///< ContentMetaInstallState. Version required_download_system_version; - u8 reserved_2[0x4]; + u8 reserved[0x4]; } ContentMetaPackagedContentMetaHeader; NXDT_ASSERT(ContentMetaPackagedContentMetaHeader, 0x20); diff --git a/include/core/nca_storage.h b/include/core/nca_storage.h index 05c7719..687ba22 100644 --- a/include/core/nca_storage.h +++ b/include/core/nca_storage.h @@ -50,9 +50,8 @@ typedef struct { } NcaStorageContext; /// Initializes a NCA storage context using a NCA FS section context, optionally providing a pointer to a base NcaStorageContext. -/// 'base_ctx' must be provided if dealing with a patch NCA. One of its storages will be set as the original substorage for the initialized NcaStorageContext's Indirect Storage. -/// This is needed to perform combined reads between a base NCA and a patch NCA. -/// 'base_ctx' shall be NULL if dealing with a base NCA. +/// 'base_ctx' shall be provided if dealing with a patch NCA with available base NCA data. This is needed to perform combined reads between a base NCA and a patch NCA. +/// 'base_ctx' shall be NULL if dealing with a base NCA *or* a patch NCA with missing base NCA data. bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx); /// Retrieves the underlying NCA FS section's hierarchical hash target layer extents. Virtual extents may be returned, depending on the base storage type. diff --git a/source/core/bktr.c b/source/core/bktr.c index b57ef36..2ae41a6 100644 --- a/source/core/bktr.c +++ b/source/core/bktr.c @@ -1138,8 +1138,8 @@ static bool bktrReadCompressedStorage(BucketTreeVisitor *visitor, void *out, u64 } /* Decompress LZ4 block. */ - int lz4_res = 0; - if ((lz4_res = LZ4_decompress_safe((char*)read_ptr, (char*)buffer, (int)compressed_data_size, (int)buffer_size)) != (int)decompressed_data_size) + int lz4_res = LZ4_decompress_safe((char*)read_ptr, (char*)buffer, (int)compressed_data_size, (int)buffer_size); + if (lz4_res != (int)decompressed_data_size) { LOG_MSG_ERROR("Failed to decompress 0x%lX-byte long compressed block! (%d).", compressed_data_size, lz4_res); free(buffer); diff --git a/source/core/cnmt.c b/source/core/cnmt.c index 7b03b83..09444f4 100644 --- a/source/core/cnmt.c +++ b/source/core/cnmt.c @@ -146,6 +146,12 @@ bool cnmtInitializeContext(ContentMetaContext *out, NcaContext *nca_ctx) goto end; } + if (out->packaged_header->content_meta_platform >= ContentMetaPlatform_Count) + { + LOG_MSG_ERROR("Invalid platform!"); + goto end; + } + if (!out->packaged_header->content_count && out->packaged_header->content_meta_type != NcmContentMetaType_SystemUpdate) { LOG_MSG_ERROR("Invalid content count!"); diff --git a/source/core/nca_storage.c b/source/core/nca_storage.c index 1369a9c..9464643 100644 --- a/source/core/nca_storage.c +++ b/source/core/nca_storage.c @@ -25,13 +25,13 @@ /* Function prototypes. */ static bool ncaStorageInitializeBucketTreeContext(BucketTreeContext **out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type); -static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx); +static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaStorageContext *base_ctx); static bool ncaStorageInitializeCompressedStorageBucketTreeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx); bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx) { if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || (nca_fs_ctx->section_type == NcaFsSectionType_PatchRomFs && \ - (!nca_fs_ctx->has_patch_indirect_layer || !nca_fs_ctx->has_patch_aes_ctr_ex_layer || nca_fs_ctx->has_sparse_layer || !base_ctx))) + (!nca_fs_ctx->has_patch_indirect_layer || !nca_fs_ctx->has_patch_aes_ctr_ex_layer || nca_fs_ctx->has_sparse_layer))) { LOG_MSG_ERROR("Invalid parameters!"); return false; @@ -61,7 +61,7 @@ bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nc /* Check if both Indirect and AesCtrEx layers are available. */ if (nca_fs_ctx->section_type == NcaFsSectionType_PatchRomFs) { - /* Initialize AesCtrEx and Indirect layers. */ + /* Initialize AesCtrEx layer. */ if (!ncaStorageInitializeBucketTreeContext(&(out->aes_ctr_ex_storage), nca_fs_ctx, BucketTreeStorageType_AesCtrEx) || \ !ncaStorageInitializeBucketTreeContext(&(out->indirect_storage), nca_fs_ctx, BucketTreeStorageType_Indirect)) goto end; @@ -69,8 +69,8 @@ bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nc if (!bktrSetRegularSubStorage(out->aes_ctr_ex_storage, nca_fs_ctx)) goto end; /* Set Indirect layer's substorages (Base + AesCtrEx). */ - if (!ncaStorageSetPatchOriginalSubStorage(out, nca_fs_ctx, base_ctx)) goto end; - if (!bktrSetBucketTreeSubStorage(out->indirect_storage, out->aes_ctr_ex_storage, 1)) goto end; + if (!ncaStorageSetPatchOriginalSubStorage(out, base_ctx) || \ + !bktrSetBucketTreeSubStorage(out->indirect_storage, out->aes_ctr_ex_storage, 1)) goto end; /* Update base storage type. */ out->base_storage_type = NcaStorageBaseStorageType_Indirect; @@ -261,21 +261,27 @@ end: return success; } -static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx) +static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaStorageContext *base_ctx) { + NcaFsSectionContext *patch_nca_fs_ctx = NULL, *base_nca_fs_ctx = NULL; NcaContext *patch_nca_ctx = NULL, *base_nca_ctx = NULL; - if (!patch_ctx || !patch_ctx->indirect_storage || !patch_ctx->aes_ctr_ex_storage || !patch_nca_fs_ctx || !ncaStorageIsValidContext(base_ctx) || \ - !(patch_nca_ctx = patch_nca_fs_ctx->nca_ctx) || !(base_nca_ctx = base_ctx->nca_fs_ctx->nca_ctx) || \ - patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \ + bool missing_base_ctx = !ncaStorageIsValidContext(base_ctx); + + bool success = false; + + if (!patch_ctx || !patch_ctx->indirect_storage || !patch_ctx->aes_ctr_ex_storage || !(patch_nca_fs_ctx = patch_ctx->indirect_storage->nca_fs_ctx) || \ + patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || !(patch_nca_ctx = patch_nca_fs_ctx->nca_ctx) || \ + (!missing_base_ctx && (!(base_nca_fs_ctx = base_ctx->nca_fs_ctx) || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || !(base_nca_ctx = base_nca_fs_ctx->nca_ctx) || \ patch_nca_ctx->header.program_id != base_nca_ctx->header.program_id || patch_nca_ctx->header.content_type != base_nca_ctx->header.content_type || \ - patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version.value < base_nca_ctx->title_version.value) + patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version.value < base_nca_ctx->title_version.value))) { LOG_MSG_ERROR("Invalid parameters!"); return false; } - bool success = false; + /* Return immediately if we passed all patch context checks, but we're missing a base context. */ + if (missing_base_ctx) return true; /* Set original substorage. */ switch(base_ctx->base_storage_type) diff --git a/source/core/romfs.c b/source/core/romfs.c index 0b2f3ea..381b7ce 100644 --- a/source/core/romfs.c +++ b/source/core/romfs.c @@ -66,7 +66,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base if (patch_nca_fs_ctx) { /* Initialize base NCA storage context. */ - if (!ncaStorageInitializeContext(patch_storage_ctx, patch_nca_fs_ctx, base_storage_ctx)) + if (!ncaStorageInitializeContext(patch_storage_ctx, patch_nca_fs_ctx, missing_base_romfs ? NULL : base_storage_ctx)) { LOG_MSG_ERROR("Failed to initialize patch NCA storage context!"); goto end;