summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-10-30 09:10:55 -0400
committerGravatar Zach Hilman2018-11-15 12:48:09 -0500
commitc0a9abc3e10ce8873dc6d553c087862c121e1736 (patch)
treebd59872015ad1e0c61c849c1935515855b112394 /src
parentldr_ro: Fully Implement LoadNro (command 0) (diff)
downloadyuzu-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.cpp107
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
413void InstallInterfaces(SM::ServiceManager& sm) { 476void InstallInterfaces(SM::ServiceManager& sm) {