diff --git a/libraries/libstratosphere/source/util/util_compression_lz4.cpp b/libraries/libstratosphere/source/util/util_compression_lz4.cpp index 05b1dfa76..46950e180 100644 --- a/libraries/libstratosphere/source/util/util_compression_lz4.cpp +++ b/libraries/libstratosphere/source/util/util_compression_lz4.cpp @@ -40,29 +40,31 @@ namespace ams::util { /* It's similar to the official lz4frame code, but has additional constraints and uses static work buffers. */ /* Evaluate if we should use lz4frame instead and implement matching frame compression. */ int DecompressLZ4Frame(size_t* out_size, void *dst, size_t dst_size, const void *src, size_t src_size, void *src_work, size_t src_work_size, void *dst_work, size_t dst_work_size) { + const u8 *src_buffer = reinterpret_cast(src); + u8 *dst_buffer = reinterpret_cast(dst); + /* Destination buffer must be behind source buffer. */ - if (dst > src) { + if (dst_buffer > src_buffer) { std::memset(dst, 0, dst_size); return -1; } - const char *src_end = static_cast(src) + src_size; - const char *dst_end = static_cast(dst) + dst_size; - - if ((src_end != dst_end) || (src_work_size != 0x10000) || (dst_work_size != 0x10000)) { + /* Size checks. */ + if (((src_buffer + src_size) != (dst_buffer + dst_size)) || (src_work_size != 0x10000) || (dst_work_size != 0x10000)) { std::memset(dst, 0, dst_size); return -1; } + /* Source size must at least include one frame header. */ if (src_size < 7) { std::memset(dst, 0, dst_size); return -2; } - const uint32_t magic = *reinterpret_cast(src); - const uint8_t flg = *reinterpret_cast(src) + 4; - const uint8_t bd = *reinterpret_cast(src) + 5; - const uint8_t header_checksum = *reinterpret_cast(src) + 6; + const u32 magic = *reinterpret_cast(src_buffer); + const u8 flg = src_buffer[4]; + const u8 bd = src_buffer[5]; + const u8 header_checksum = src_buffer[6]; /* Check for LZ4F_MAGICNUMBER, validate flags and block size. */ if ((magic != 0x184D2204) || ((flg & 1) != 0) || (flg != 0x60) || (bd != 0x40)) { @@ -73,8 +75,8 @@ namespace ams::util { /* TODO: Implement header checksum validation. This requires xxhash, do we want to have xxhash in util? */ AMS_UNUSED(header_checksum); - uint32_t src_pos = 7; /* Skip the header. */ - uint32_t dst_pos = 0; + u32 src_pos = 7; /* Skip the header. */ + u32 dst_pos = 0; /* Destination size is too small. */ if (dst_size < src_pos) { @@ -89,17 +91,17 @@ namespace ams::util { return -4; } - const char* src_data_block = reinterpret_cast(src) + src_pos; - char* dst_data_block = reinterpret_cast(dst) + dst_pos; + src_buffer += src_pos; + dst_buffer += dst_pos; - const uint32_t block_header = *reinterpret_cast(src_data_block); + const u32 block_header = *reinterpret_cast(src_buffer); /* We've reached the end. */ if (!block_header) { break; } - const uint32_t block_size = block_header & 0x7FFFFFFF; + const u32 block_size = block_header & 0x7FFFFFFF; /* Block size is invalid. */ if (block_size > 0x10000) { @@ -108,20 +110,20 @@ namespace ams::util { } /* Advance the data block. */ - src_data_block += sizeof(block_header); + src_buffer += sizeof(block_header); /* Block size is invalid. */ - if ((dst_end - src_data_block) < block_size) { + if (((dst_buffer + dst_size) - src_buffer) < block_size) { std::memset(dst, 0, dst_size); return -4; } /* Copy the data block to the source work buffer. */ - std::memcpy(static_cast(src_work), src_data_block, block_size); + std::memcpy(static_cast(src_work), src_buffer, block_size); if ((block_header & 0x80000000) != 0) { /* Block size is invalid. */ - if ((dst_end - dst_data_block) < block_size) { + if (dst_size < block_size) { std::memset(dst, 0, dst_size); return -4; } @@ -136,10 +138,10 @@ namespace ams::util { } /* This is a raw block, copy it. */ - std::memmove(dst_data_block, src_data_block, block_size); + std::memmove(dst_buffer, src_buffer, block_size); } else { /* This is a compressed block, decompress it. */ - int decompressed_size = LZ4_decompress_safe(reinterpret_cast(src_work), reinterpret_cast(dst_work), static_cast(src_work_size), static_cast(dst_work_size)); + int decompressed_size = LZ4_decompress_safe(reinterpret_cast(src_work), reinterpret_cast(dst_work), static_cast(block_size), 0x10000); /* Decompressed size is invalid. */ if (decompressed_size < 1) { @@ -148,7 +150,7 @@ namespace ams::util { } /* Decompressed size is invalid. */ - if ((dst_end - dst_data_block) < decompressed_size) { + if (dst_size < static_cast(decompressed_size)) { std::memset(dst, 0, dst_size); return -4; } @@ -163,7 +165,7 @@ namespace ams::util { } /* Copy the decompressed data back. */ - std::memcpy(dst_data_block, static_cast(dst_work), decompressed_size); + std::memcpy(dst_buffer, static_cast(dst_work), decompressed_size); } /* We've exceeded the destination's size. */ diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 63b93d8f8..ef1a6afb1 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -134,8 +134,11 @@ namespace ams::ro::impl { m_process_handle = os::InvalidNativeHandle; m_process_id = os::InvalidProcessId; - + m_in_use = false; + + std::memset(m_decompression_src_work_buffer, 0, sizeof(m_decompression_src_work_buffer)); + std::memset(m_decompression_dst_work_buffer, 0, sizeof(m_decompression_dst_work_buffer)); } os::NativeHandle GetProcessHandle() const { @@ -365,7 +368,7 @@ namespace ams::ro::impl { R_SUCCEED(); } - Result DecompressNro(u8 *mapped_nro, u64 expected_nro_size, const NroHeader *nro_header, u32 compressed_size) { + Result DecompressNro(u8 *mapped_nro, u64 total_size, const NroHeader *nro_header, u32 compressed_size) { /* Read sizes from header. */ const u64 rw_ofs = nro_header->GetRwOffset(); const u64 rw_size = nro_header->GetRwSize(); @@ -373,10 +376,10 @@ namespace ams::ro::impl { /* Divide the mapped NRO into regions devised for the decompression. */ u8 *nro_aligned_start = static_cast(mapped_nro + 0x1000); - const u8 *nro_expected_end = static_cast(mapped_nro + expected_nro_size); + const u8 *nro_expected_end = static_cast(mapped_nro + total_size); const u64 nro_src_buffer_size = compressed_size - 0x1000; - u8 *nro_src_buffer = static_cast(mapped_nro + expected_nro_size - nro_src_buffer_size); - const u64 nro_dst_buffer_size = expected_nro_size - 0xC00; + u8 *nro_src_buffer = static_cast(mapped_nro + total_size - nro_src_buffer_size); + const u64 nro_dst_buffer_size = total_size - 0xC00; u8 *nro_dst_buffer = static_cast(mapped_nro + 0xC00); /* Copy to source buffer. */ @@ -384,7 +387,7 @@ namespace ams::ro::impl { /* Decompress. */ u64 decompressed_size = 0; - R_UNLESS(util::DecompressLZ4Frame(std::addressof(decompressed_size), nro_dst_buffer, nro_dst_buffer_size, nro_src_buffer, nro_src_buffer_size, m_decompression_src_work_buffer, sizeof(m_decompression_src_work_buffer), m_decompression_dst_work_buffer, sizeof(m_decompression_dst_work_buffer)), ro::ResultInvalidNro()); + R_UNLESS(util::DecompressLZ4Frame(std::addressof(decompressed_size), nro_dst_buffer, nro_dst_buffer_size, nro_src_buffer, nro_src_buffer_size, m_decompression_src_work_buffer, sizeof(m_decompression_src_work_buffer), m_decompression_dst_work_buffer, sizeof(m_decompression_dst_work_buffer)) == 0, ro::ResultInvalidNro()); /* Find the BSS. */ u8 *nro_expected_bss_start = static_cast(mapped_nro + rw_ofs + rw_size); @@ -423,15 +426,15 @@ namespace ams::ro::impl { R_SUCCEED(); } - Result ParseNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, bool *out_aligned_header, bool *out_is_compress, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) { + Result ParseNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 *out_bss_size, bool *out_aligned_header, bool *out_is_compress, u64 base_address, u64 total_size, u64 expected_nro_size, u64 expected_bss_size) { /* Map the NRO. */ void *mapped_memory = nullptr; - R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size, ro::impl::GenerateSecureRandom)) { + R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, total_size, ro::impl::GenerateSecureRandom)) { R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) } R_END_TRY_CATCH; - + /* When we're done, unmap the memory. */ - ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, expected_nro_size); }; + ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, total_size); }; /* ValidateNro */ NroHeader *nro_header = nullptr; @@ -441,7 +444,7 @@ namespace ams::ro::impl { /* DecompressNro */ if (nro_header->IsCompress()) { - R_TRY(this->DecompressNro(static_cast(mapped_memory), expected_nro_size, nro_header, additional_nro_header_info.compressed_size)); + R_TRY(this->DecompressNro(static_cast(mapped_memory), total_size, nro_header, additional_nro_header_info.compressed_size)); /* CheckAdditionalHeaderHash */ if (nro_header->GetAdditionalHeaderOffset() != 0) { @@ -458,6 +461,7 @@ namespace ams::ro::impl { *out_rx_size = nro_header->GetTextSize(); *out_ro_size = nro_header->GetRoSize(); *out_rw_size = nro_header->GetRwSize(); + *out_bss_size = nro_header->GetBssSize(); *out_aligned_header = nro_header->IsAlignedHeader(); *out_is_compress = nro_header->IsCompress(); R_SUCCEED(); @@ -706,28 +710,28 @@ namespace ams::ro::impl { ON_RESULT_FAILURE { R_DISCARD(UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size)); }; /* Parse the NRO. */ - u64 rx_size = 0, ro_size = 0, rw_size = 0; - bool aligned_header = false; - bool is_compress = false; - R_TRY(context->ParseNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), std::addressof(aligned_header), std::addressof(is_compress), nro_info->base_address, nro_size, bss_size)); + u64 nro_rx_size = 0, nro_ro_size = 0, nro_rw_size = 0, nro_bss_size = 0; + bool nro_aligned_header = false; + bool nro_is_compress = false; + R_TRY(context->ParseNro(std::addressof(nro_info->module_id), std::addressof(nro_rx_size), std::addressof(nro_ro_size), std::addressof(nro_rw_size), std::addressof(nro_bss_size), std::addressof(nro_aligned_header), std::addressof(nro_is_compress), nro_info->base_address, total_size, nro_size, bss_size)); /* Set NRO perms. */ - R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size, aligned_header)); - + R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, nro_rx_size, nro_ro_size, nro_rw_size + nro_bss_size, nro_aligned_header)); + context->SetNroInfoInUse(nro_info, true); nro_info->nro_heap_address = nro_address; nro_info->nro_heap_size = nro_size; nro_info->bss_heap_address = bss_address; nro_info->bss_heap_size = bss_size; if (hos::GetVersion() >= hos::Version_23_0_0) { - nro_info->code_size = rx_size; - nro_info->ro_size = ro_size; + nro_info->code_size = nro_rx_size; + nro_info->ro_size = nro_ro_size; } else { - nro_info->code_size = rx_size + ro_size; + nro_info->code_size = nro_rx_size + nro_ro_size; nro_info->ro_size = 0; } - nro_info->rw_size = rw_size; - nro_info->is_compress = is_compress; + nro_info->rw_size = nro_rw_size; + nro_info->is_compress = nro_is_compress; *out_address = nro_info->base_address; R_SUCCEED(); } @@ -770,7 +774,11 @@ namespace ams::ro::impl { context->SetNroInfoInUse(nro_info, false); std::memset(nro_info, 0, sizeof(*nro_info)); } - R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.code_size + nro_backup.rw_size, nro_backup.bss_heap_address, nro_backup.bss_heap_size)); + if (hos::GetVersion() >= hos::Version_23_0_0) { + R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.nro_heap_size, nro_backup.bss_heap_address, nro_backup.bss_heap_size)); + } else { + R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.code_size + nro_backup.rw_size, nro_backup.bss_heap_address, nro_backup.bss_heap_size)); + } } /* Debug service implementations. */