ro/util: last round of fixes, now fully matches official behavior
This commit is contained in:
@@ -45,19 +45,24 @@ namespace ams::util {
|
|||||||
|
|
||||||
/* Destination buffer must be behind source buffer. */
|
/* Destination buffer must be behind source buffer. */
|
||||||
if (dst_buffer > src_buffer) {
|
if (dst_buffer > src_buffer) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const u8 *src_buffer_start = src_buffer;
|
||||||
|
const u8 *src_buffer_end = src_buffer + src_size;
|
||||||
|
const u8 *dst_buffer_start = dst_buffer;
|
||||||
|
const u8 *dst_buffer_end = dst_buffer + dst_size;
|
||||||
|
|
||||||
/* Size checks. */
|
/* Size checks. */
|
||||||
if (((src_buffer + src_size) != (dst_buffer + dst_size)) || (src_work_size != 0x10000) || (dst_work_size != 0x10000)) {
|
if ((src_buffer_end != dst_buffer_end) || (src_work_size != 0x10000) || (dst_work_size != 0x10000)) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Source size must at least include one frame header. */
|
/* Source size must at least include one frame header. */
|
||||||
if (src_size < 7) {
|
if (src_size < 7) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,34 +73,34 @@ namespace ams::util {
|
|||||||
|
|
||||||
/* Check for LZ4F_MAGICNUMBER, validate flags and block size. */
|
/* Check for LZ4F_MAGICNUMBER, validate flags and block size. */
|
||||||
if ((magic != 0x184D2204) || ((flg & 1) != 0) || (flg != 0x60) || (bd != 0x40)) {
|
if ((magic != 0x184D2204) || ((flg & 1) != 0) || (flg != 0x60) || (bd != 0x40)) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Implement header checksum validation. This requires xxhash, do we want to have xxhash in util? */
|
/* TODO: Implement header checksum validation. This requires xxhash, do we want to have xxhash in util? */
|
||||||
AMS_UNUSED(header_checksum);
|
AMS_UNUSED(header_checksum);
|
||||||
|
|
||||||
u32 src_pos = 7; /* Skip the header. */
|
/* Skip the frame header. */
|
||||||
u32 dst_pos = 0;
|
src_buffer += 7;
|
||||||
|
|
||||||
/* Destination size is too small. */
|
/* Destination is too small. */
|
||||||
if (dst_size < src_pos) {
|
if (src_buffer >= dst_buffer_end) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Not enough space left. */
|
/* Not enough space left. */
|
||||||
if ((dst_size - src_pos) < 4) {
|
if ((dst_buffer_end - src_buffer) < 4) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
src_buffer += src_pos;
|
|
||||||
dst_buffer += dst_pos;
|
|
||||||
|
|
||||||
const u32 block_header = *reinterpret_cast<const u32 *>(src_buffer);
|
const u32 block_header = *reinterpret_cast<const u32 *>(src_buffer);
|
||||||
|
|
||||||
|
/* Advance the block. */
|
||||||
|
src_buffer += sizeof(block_header);
|
||||||
|
|
||||||
/* We've reached the end. */
|
/* We've reached the end. */
|
||||||
if (!block_header) {
|
if (!block_header) {
|
||||||
break;
|
break;
|
||||||
@@ -105,16 +110,13 @@ namespace ams::util {
|
|||||||
|
|
||||||
/* Block size is invalid. */
|
/* Block size is invalid. */
|
||||||
if (block_size > 0x10000) {
|
if (block_size > 0x10000) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advance the data block. */
|
|
||||||
src_buffer += sizeof(block_header);
|
|
||||||
|
|
||||||
/* Block size is invalid. */
|
/* Block size is invalid. */
|
||||||
if (((dst_buffer + dst_size) - src_buffer) < block_size) {
|
if ((dst_buffer_end - src_buffer) < block_size) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,65 +125,67 @@ namespace ams::util {
|
|||||||
|
|
||||||
if ((block_header & 0x80000000) != 0) {
|
if ((block_header & 0x80000000) != 0) {
|
||||||
/* Block size is invalid. */
|
/* Block size is invalid. */
|
||||||
if (dst_size < block_size) {
|
if ((dst_buffer_end - dst_buffer) < block_size) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst_pos += block_size;
|
|
||||||
src_pos += block_size;
|
|
||||||
|
|
||||||
/* Destination is overrunning source. */
|
/* Destination is overrunning source. */
|
||||||
if (dst_pos > src_pos) {
|
if ((dst_buffer + block_size) > (src_buffer + block_size)) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is a raw block, copy it. */
|
/* This is a raw block, copy it. */
|
||||||
std::memmove(dst_buffer, src_buffer, block_size);
|
std::memmove(dst_buffer, src_buffer, block_size);
|
||||||
|
|
||||||
|
/* Advance buffers. */
|
||||||
|
dst_buffer += block_size;
|
||||||
|
src_buffer += block_size;
|
||||||
} else {
|
} else {
|
||||||
/* This is a compressed block, decompress it. */
|
/* This is a compressed block, decompress it. */
|
||||||
int decompressed_size = LZ4_decompress_safe(reinterpret_cast<const char *>(src_work), reinterpret_cast<char *>(dst_work), static_cast<int>(block_size), 0x10000);
|
int decompressed_size = LZ4_decompress_safe(reinterpret_cast<const char *>(src_work), reinterpret_cast<char *>(dst_work), static_cast<int>(block_size), 0x10000);
|
||||||
|
|
||||||
/* Decompressed size is invalid. */
|
/* Decompressed size is invalid. */
|
||||||
if (decompressed_size < 1) {
|
if (decompressed_size < 1) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decompressed size is invalid. */
|
/* Decompressed size is invalid. */
|
||||||
if (dst_size < static_cast<size_t>(decompressed_size)) {
|
if (static_cast<size_t>(dst_buffer_end - dst_buffer) < static_cast<size_t>(decompressed_size)) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst_pos += decompressed_size;
|
|
||||||
src_pos += block_size;
|
|
||||||
|
|
||||||
/* Destination is overrunning source. */
|
/* Destination is overrunning source. */
|
||||||
if (dst_pos > src_pos) {
|
if ((dst_buffer + decompressed_size) > (src_buffer + block_size)) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the decompressed data back. */
|
/* Copy the decompressed data back. */
|
||||||
std::memcpy(dst_buffer, static_cast<u8 *>(dst_work), decompressed_size);
|
std::memcpy(dst_buffer, static_cast<u8 *>(dst_work), decompressed_size);
|
||||||
|
|
||||||
|
/* Advance buffers. */
|
||||||
|
dst_buffer += decompressed_size;
|
||||||
|
src_buffer += block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We've exceeded the destination's size. */
|
/* We've exceeded the destination's size. */
|
||||||
if (src_pos >= dst_size) {
|
if (src_buffer >= dst_buffer_end) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We didn't consume all data. */
|
/* We didn't consume all data. */
|
||||||
if (src_pos != src_size) {
|
if (static_cast<size_t>(src_buffer - src_buffer_start) != src_size) {
|
||||||
std::memset(dst, 0, dst_size);
|
std::memset(dst_buffer, 0, dst_size);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_size = dst_pos;
|
*out_size = dst_buffer - dst_buffer_start;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,6 +449,7 @@ namespace ams::ro::impl {
|
|||||||
/* CheckAdditionalHeaderHash */
|
/* CheckAdditionalHeaderHash */
|
||||||
if (nro_header->GetAdditionalHeaderOffset() != 0) {
|
if (nro_header->GetAdditionalHeaderOffset() != 0) {
|
||||||
R_TRY(this->CheckAdditionalHeaderHash(static_cast<u8 *>(mapped_memory), std::addressof(additional_nro_header_hash), sizeof(additional_nro_header_hash), nro_header->GetRwOffset(), nro_header->GetRwSize()));
|
R_TRY(this->CheckAdditionalHeaderHash(static_cast<u8 *>(mapped_memory), std::addressof(additional_nro_header_hash), sizeof(additional_nro_header_hash), nro_header->GetRwOffset(), nro_header->GetRwSize()));
|
||||||
|
ON_RESULT_FAILURE { std::memset(static_cast<u8 *>(mapped_memory) + 0x1000, 0, total_size - 0x1000); };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,16 +708,17 @@ namespace ams::ro::impl {
|
|||||||
|
|
||||||
/* Map the NRO. */
|
/* Map the NRO. */
|
||||||
R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size));
|
R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size));
|
||||||
ON_RESULT_FAILURE { R_DISCARD(UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size)); };
|
|
||||||
|
|
||||||
/* Parse the NRO. */
|
/* Parse the NRO. */
|
||||||
u64 nro_rx_size = 0, nro_ro_size = 0, nro_rw_size = 0, nro_bss_size = 0;
|
u64 nro_rx_size = 0, nro_ro_size = 0, nro_rw_size = 0, nro_bss_size = 0;
|
||||||
bool nro_aligned_header = false;
|
bool nro_aligned_header = false;
|
||||||
bool nro_is_compress = 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));
|
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));
|
||||||
|
ON_RESULT_FAILURE { R_DISCARD(UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size)); };
|
||||||
|
|
||||||
/* Set NRO perms. */
|
/* Set NRO perms. */
|
||||||
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, nro_rx_size, nro_ro_size, nro_rw_size + nro_bss_size, nro_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));
|
||||||
|
ON_RESULT_FAILURE_2 { if (!nro_is_compress) { R_DISCARD(UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size)); } };
|
||||||
|
|
||||||
context->SetNroInfoInUse(nro_info, true);
|
context->SetNroInfoInUse(nro_info, true);
|
||||||
nro_info->nro_heap_address = nro_address;
|
nro_info->nro_heap_address = nro_address;
|
||||||
|
|||||||
Reference in New Issue
Block a user