diff options
| author | 2018-10-30 09:10:55 -0400 | |
|---|---|---|
| committer | 2018-11-15 12:48:09 -0500 | |
| commit | c0a9abc3e10ce8873dc6d553c087862c121e1736 (patch) | |
| tree | bd59872015ad1e0c61c849c1935515855b112394 /src | |
| parent | ldr_ro: Fully Implement LoadNro (command 0) (diff) | |
| download | yuzu-c0a9abc3e10ce8873dc6d553c087862c121e1736.tar.gz yuzu-c0a9abc3e10ce8873dc6d553c087862c121e1736.tar.xz yuzu-c0a9abc3e10ce8873dc6d553c087862c121e1736.zip | |
ldr_ro: Implement UnloadNro (command 1)
Includes actual unmapping and address error checking.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/ldr/ldr.cpp | 107 |
1 files changed, 85 insertions, 22 deletions
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 11b8a02e2..5f020cbe0 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 7 | #include <mbedtls/sha256.h> | 7 | #include <mbedtls/sha256.h> |
| 8 | 8 | ||
| 9 | #include "common/alignment.h" | ||
| 9 | #include "common/hex_util.h" | 10 | #include "common/hex_util.h" |
| 10 | #include "core/hle/ipc_helpers.h" | 11 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| @@ -98,7 +99,7 @@ public: | |||
| 98 | {0, &RelocatableObject::LoadNro, "LoadNro"}, | 99 | {0, &RelocatableObject::LoadNro, "LoadNro"}, |
| 99 | {1, &RelocatableObject::UnloadNro, "UnloadNro"}, | 100 | {1, &RelocatableObject::UnloadNro, "UnloadNro"}, |
| 100 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, | 101 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, |
| 101 | {3, nullptr, "UnloadNrr"}, | 102 | {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, |
| 102 | {4, &RelocatableObject::Initialize, "Initialize"}, | 103 | {4, &RelocatableObject::Initialize, "Initialize"}, |
| 103 | }; | 104 | }; |
| 104 | // clang-format on | 105 | // clang-format on |
| @@ -128,7 +129,7 @@ public: | |||
| 128 | } | 129 | } |
| 129 | 130 | ||
| 130 | // NRR Address does not fall on 0x1000 byte boundary | 131 | // NRR Address does not fall on 0x1000 byte boundary |
| 131 | if ((nrr_addr & 0xFFF) != 0) { | 132 | if (!Common::Is4KBAligned(nrr_addr)) { |
| 132 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 133 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); |
| 133 | IPC::ResponseBuilder rb{ctx, 2}; | 134 | IPC::ResponseBuilder rb{ctx, 2}; |
| 134 | rb.Push(ERROR_INVALID_ALIGNMENT); | 135 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| @@ -136,7 +137,7 @@ public: | |||
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | // NRR Size is zero or causes overflow | 139 | // NRR Size is zero or causes overflow |
| 139 | if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || (nrr_size & 0xFFF) != 0) { | 140 | if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { |
| 140 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", | 141 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", |
| 141 | nrr_addr, nrr_size); | 142 | nrr_addr, nrr_size); |
| 142 | IPC::ResponseBuilder rb{ctx, 2}; | 143 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -201,7 +202,7 @@ public: | |||
| 201 | rp.Skip(2, false); | 202 | rp.Skip(2, false); |
| 202 | const auto nrr_addr{rp.Pop<VAddr>()}; | 203 | const auto nrr_addr{rp.Pop<VAddr>()}; |
| 203 | 204 | ||
| 204 | if ((nrr_addr & 0xFFF) != 0) { | 205 | if (!Common::Is4KBAligned(nrr_addr)) { |
| 205 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 206 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); |
| 206 | IPC::ResponseBuilder rb{ctx, 2}; | 207 | IPC::ResponseBuilder rb{ctx, 2}; |
| 207 | rb.Push(ERROR_INVALID_ALIGNMENT); | 208 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| @@ -247,7 +248,7 @@ public: | |||
| 247 | } | 248 | } |
| 248 | 249 | ||
| 249 | // NRO Address does not fall on 0x1000 byte boundary | 250 | // NRO Address does not fall on 0x1000 byte boundary |
| 250 | if ((nro_addr & 0xFFF) != 0) { | 251 | if (!Common::Is4KBAligned(nro_addr)) { |
| 251 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); | 252 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); |
| 252 | IPC::ResponseBuilder rb{ctx, 2}; | 253 | IPC::ResponseBuilder rb{ctx, 2}; |
| 253 | rb.Push(ERROR_INVALID_ALIGNMENT); | 254 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| @@ -255,9 +256,12 @@ public: | |||
| 255 | } | 256 | } |
| 256 | 257 | ||
| 257 | // NRO Size or BSS Size is zero or causes overflow | 258 | // NRO Size or BSS Size is zero or causes overflow |
| 258 | if (nro_addr + nro_size <= nro_addr || nro_size == 0 || (nro_size & 0xFFF) != 0 || | 259 | const auto nro_size_valid = |
| 259 | (bss_size != 0 && bss_addr + bss_size <= bss_addr) || | 260 | nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); |
| 260 | (std::numeric_limits<u64>::max() - nro_size < bss_size)) { | 261 | const auto bss_size_valid = std::numeric_limits<u64>::max() - nro_size >= bss_size && |
| 262 | (bss_size == 0 || bss_addr + bss_size > bss_addr); | ||
| 263 | |||
| 264 | if (!nro_size_valid || !bss_size_valid) { | ||
| 261 | LOG_ERROR(Service_LDR, | 265 | LOG_ERROR(Service_LDR, |
| 262 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " | 266 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " |
| 263 | "bss_address={:016X}, bss_size={:016X})", | 267 | "bss_address={:016X}, bss_size={:016X})", |
| @@ -275,9 +279,9 @@ public: | |||
| 275 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); | 279 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); |
| 276 | 280 | ||
| 277 | // NRO Hash is already loaded | 281 | // NRO Hash is already loaded |
| 278 | if (std::find_if(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) { | 282 | if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) { |
| 279 | return info.second.hash == hash; | 283 | return info.second.hash == hash; |
| 280 | }) != nro.end()) { | 284 | })) { |
| 281 | LOG_ERROR(Service_LDR, "NRO is already loaded!"); | 285 | LOG_ERROR(Service_LDR, "NRO is already loaded!"); |
| 282 | IPC::ResponseBuilder rb{ctx, 2}; | 286 | IPC::ResponseBuilder rb{ctx, 2}; |
| 283 | rb.Push(ERROR_ALREADY_LOADED); | 287 | rb.Push(ERROR_ALREADY_LOADED); |
| @@ -285,11 +289,7 @@ public: | |||
| 285 | } | 289 | } |
| 286 | 290 | ||
| 287 | // NRO Hash is not in any loaded NRR | 291 | // NRO Hash is not in any loaded NRR |
| 288 | if (std::find_if(nrr.begin(), nrr.end(), | 292 | if (!IsValidNROHash(hash)) { |
| 289 | [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { | ||
| 290 | return std::find(p.second.begin(), p.second.end(), hash) != | ||
| 291 | p.second.end(); | ||
| 292 | }) == nrr.end()) { | ||
| 293 | LOG_ERROR(Service_LDR, | 293 | LOG_ERROR(Service_LDR, |
| 294 | "NRO hash is not present in any currently loaded NRRs (hash={})!", | 294 | "NRO hash is not present in any currently loaded NRRs (hash={})!", |
| 295 | Common::HexArrayToString(hash)); | 295 | Common::HexArrayToString(hash)); |
| @@ -301,12 +301,7 @@ public: | |||
| 301 | NROHeader header; | 301 | NROHeader header; |
| 302 | std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); | 302 | std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); |
| 303 | 303 | ||
| 304 | if (header.magic != Common::MakeMagic('N', 'R', 'O', '0') || header.nro_size != nro_size || | 304 | if (!IsValidNRO(header, nro_size, bss_size)) { |
| 305 | header.bss_size != bss_size || | ||
| 306 | header.ro_offset != header.text_offset + header.text_size || | ||
| 307 | header.rw_offset != header.ro_offset + header.ro_size || | ||
| 308 | nro_size != header.rw_offset + header.rw_size || (header.text_size & 0xFFF) != 0 || | ||
| 309 | (header.ro_size & 0xFFF) != 0 || (header.rw_size & 0xFFF) != 0) { | ||
| 310 | LOG_ERROR(Service_LDR, "NRO was invalid!"); | 305 | LOG_ERROR(Service_LDR, "NRO was invalid!"); |
| 311 | IPC::ResponseBuilder rb{ctx, 2}; | 306 | IPC::ResponseBuilder rb{ctx, 2}; |
| 312 | rb.Push(ERROR_INVALID_NRO); | 307 | rb.Push(ERROR_INVALID_NRO); |
| @@ -314,7 +309,7 @@ public: | |||
| 314 | } | 309 | } |
| 315 | 310 | ||
| 316 | // Load NRO as new executable module | 311 | // Load NRO as new executable module |
| 317 | auto process = Core::CurrentProcess(); | 312 | auto* process = Core::CurrentProcess(); |
| 318 | auto& vm_manager = process->VMManager(); | 313 | auto& vm_manager = process->VMManager(); |
| 319 | auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); | 314 | auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); |
| 320 | 315 | ||
| @@ -348,6 +343,57 @@ public: | |||
| 348 | rb.Push(RESULT_SUCCESS); | 343 | rb.Push(RESULT_SUCCESS); |
| 349 | rb.Push(*map_address); | 344 | rb.Push(*map_address); |
| 350 | } | 345 | } |
| 346 | |||
| 347 | void UnloadNro(Kernel::HLERequestContext& ctx) { | ||
| 348 | IPC::RequestParser rp{ctx}; | ||
| 349 | rp.Skip(2, false); | ||
| 350 | const VAddr mapped_addr{rp.PopRaw<VAddr>()}; | ||
| 351 | const VAddr heap_addr{rp.PopRaw<VAddr>()}; | ||
| 352 | |||
| 353 | if (!initialized) { | ||
| 354 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | ||
| 355 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 356 | rb.Push(ERROR_NOT_INITIALIZED); | ||
| 357 | return; | ||
| 358 | } | ||
| 359 | |||
| 360 | if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { | ||
| 361 | LOG_ERROR(Service_LDR, | ||
| 362 | "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " | ||
| 363 | "bss_addr={:016X})!", | ||
| 364 | mapped_addr, heap_addr); | ||
| 365 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 366 | rb.Push(ERROR_INVALID_ALIGNMENT); | ||
| 367 | return; | ||
| 368 | } | ||
| 369 | |||
| 370 | const auto iter = nro.find(mapped_addr); | ||
| 371 | if (iter == nro.end()) { | ||
| 372 | LOG_ERROR(Service_LDR, | ||
| 373 | "The NRO attempting to unmap was not mapped or has an invalid address " | ||
| 374 | "(actual {:016X})!", | ||
| 375 | mapped_addr); | ||
| 376 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 377 | rb.Push(ERROR_INVALID_NRO_ADDRESS); | ||
| 378 | return; | ||
| 379 | } | ||
| 380 | |||
| 381 | auto* process = Core::CurrentProcess(); | ||
| 382 | auto& vm_manager = process->VMManager(); | ||
| 383 | const auto& nro_size = iter->second.size; | ||
| 384 | |||
| 385 | ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size, | ||
| 386 | Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); | ||
| 387 | ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); | ||
| 388 | |||
| 389 | Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); | ||
| 390 | Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); | ||
| 391 | Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); | ||
| 392 | Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); | ||
| 393 | |||
| 394 | nro.erase(iter); | ||
| 395 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 396 | rb.Push(RESULT_SUCCESS); | ||
| 351 | } | 397 | } |
| 352 | 398 | ||
| 353 | void Initialize(Kernel::HLERequestContext& ctx) { | 399 | void Initialize(Kernel::HLERequestContext& ctx) { |
| @@ -408,6 +454,23 @@ private: | |||
| 408 | 454 | ||
| 409 | std::map<VAddr, NROInfo> nro; | 455 | std::map<VAddr, NROInfo> nro; |
| 410 | std::map<VAddr, std::vector<SHA256Hash>> nrr; | 456 | std::map<VAddr, std::vector<SHA256Hash>> nrr; |
| 457 | |||
| 458 | bool IsValidNROHash(const SHA256Hash& hash) { | ||
| 459 | return std::any_of( | ||
| 460 | nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { | ||
| 461 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); | ||
| 462 | }); | ||
| 463 | } | ||
| 464 | |||
| 465 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | ||
| 466 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && | ||
| 467 | header.nro_size == nro_size && header.bss_size == bss_size && | ||
| 468 | header.ro_offset == header.text_offset + header.text_size && | ||
| 469 | header.rw_offset == header.ro_offset + header.ro_size && | ||
| 470 | nro_size == header.rw_offset + header.rw_size && | ||
| 471 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && | ||
| 472 | Common::Is4KBAligned(header.rw_size); | ||
| 473 | } | ||
| 411 | }; | 474 | }; |
| 412 | 475 | ||
| 413 | void InstallInterfaces(SM::ServiceManager& sm) { | 476 | void InstallInterfaces(SM::ServiceManager& sm) { |