aboutsummaryrefslogtreecommitdiff
path: root/src/core/file_sys/program_metadata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys/program_metadata.cpp')
-rw-r--r--src/core/file_sys/program_metadata.cpp55
1 files changed, 51 insertions, 4 deletions
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 63ca9d3fe3..b528523e67 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -20,6 +20,10 @@ ProgramMetadata::ProgramMetadata() = default;
ProgramMetadata::~ProgramMetadata() = default;
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
+ uses_extended_acid = false;
+ if (!file) {
+ return Loader::ResultStatus::ErrorBadNPDMHeader;
+ }
const std::size_t total_size = file->GetSize();
if (total_size < sizeof(Header)) {
return Loader::ResultStatus::ErrorBadNPDMHeader;
@@ -29,17 +33,55 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
return Loader::ResultStatus::ErrorBadNPDMHeader;
}
- if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
+ if (npdm_header.magic != std::array{'M', 'E', 'T', 'A'}) {
+ return Loader::ResultStatus::ErrorBadNPDMHeader;
+ }
+
+ const auto contains = [](u64 offset, u64 size, u64 limit) {
+ return offset <= limit && size <= limit - offset;
+ };
+ const u64 acid_offset = npdm_header.acid_offset;
+ const u64 acid_size = npdm_header.acid_size;
+ const u64 aci_offset = npdm_header.aci_offset;
+ const u64 aci_size = npdm_header.aci_size;
+ if (acid_offset < sizeof(Header) || !contains(acid_offset, acid_size, total_size)) {
return Loader::ResultStatus::ErrorBadACIDHeader;
}
- if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
+ const auto read_acid = [&](u64 body_offset) {
+ return contains(body_offset, sizeof(AcidHeader), acid_size) &&
+ file->ReadObject(&acid_header, acid_offset + body_offset) == sizeof(AcidHeader) &&
+ acid_header.magic == std::array{'A', 'C', 'I', 'D'};
+ };
+ if (!read_acid(0x200)) {
+ if (!read_acid(0x300)) {
+ return Loader::ResultStatus::ErrorBadACIDHeader;
+ }
+ uses_extended_acid = true;
+ }
+
+ if (aci_offset < sizeof(Header) || !contains(aci_offset, aci_size, total_size) ||
+ aci_size < sizeof(AciHeader) ||
+ sizeof(AciHeader) != file->ReadObject(&aci_header, aci_offset) ||
+ aci_header.magic != std::array{'A', 'C', 'I', '0'}) {
return Loader::ResultStatus::ErrorBadACIHeader;
}
+ constexpr u64 fac_read_size = 4 + sizeof(FileAccessControl::permissions) +
+ sizeof(FileAccessControl::unknown);
+ if (acid_header.fac_size < fac_read_size ||
+ !contains(acid_header.fac_offset, acid_header.fac_size, acid_size)) {
+ return Loader::ResultStatus::ErrorBadFileAccessControl;
+ }
+ constexpr u64 fah_read_size = 4 + sizeof(FileAccessHeader::permissions) + 4 * sizeof(u32);
+ if (aci_header.fah_size < fah_read_size ||
+ !contains(aci_header.fah_offset, aci_header.fah_size, aci_size)) {
+ return Loader::ResultStatus::ErrorBadFileAccessHeader;
+ }
+
// Load acid_file_access per-component instead of the entire struct, since this struct does not
// reflect the layout of the real data.
- std::size_t current_offset = acid_header.fac_offset;
+ std::size_t current_offset = acid_offset + acid_header.fac_offset;
if (sizeof(FileAccessControl::version) != file->ReadBytes(&acid_file_access.version,
sizeof(FileAccessControl::version),
current_offset)) {
@@ -57,7 +99,7 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
}
// Load aci_file_access per-component instead of the entire struct, same as acid_file_access
- current_offset = aci_header.fah_offset;
+ current_offset = aci_offset + aci_header.fah_offset;
if (sizeof(FileAccessHeader::version) != file->ReadBytes(&aci_file_access.version,
sizeof(FileAccessHeader::version),
current_offset)) {
@@ -89,6 +131,10 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
return Loader::ResultStatus::ErrorBadFileAccessHeader;
}
+ if (aci_header.kac_size % sizeof(u32) != 0 ||
+ !contains(aci_header.kac_offset, aci_header.kac_size, aci_size)) {
+ return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
+ }
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
const u64 read_size = aci_header.kac_size;
const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset;
@@ -128,6 +174,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
u32 main_thread_stack_size, u64 title_id,
u64 filesystem_permissions, u32 system_resource_size,
KernelCapabilityDescriptors capabilities) {
+ uses_extended_acid = false;
npdm_header.has_64_bit_instructions.Assign(is_64_bit);
npdm_header.address_space_type.Assign(address_space);
npdm_header.main_thread_priority = static_cast<u8>(main_thread_prio);