diff options
| -rw-r--r-- | src/core/hle/service/ldr/ldr.cpp | 150 |
1 files changed, 85 insertions, 65 deletions
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d65693fc7..609102f2c 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -93,12 +93,18 @@ public: | |||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | void LoadNrr(Kernel::HLERequestContext& ctx) { | 95 | void LoadNrr(Kernel::HLERequestContext& ctx) { |
| 96 | struct Parameters { | ||
| 97 | u64_le process_id; | ||
| 98 | u64_le nrr_address; | ||
| 99 | u64_le nrr_size; | ||
| 100 | }; | ||
| 101 | |||
| 96 | IPC::RequestParser rp{ctx}; | 102 | IPC::RequestParser rp{ctx}; |
| 97 | rp.Skip(2, false); | 103 | const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>(); |
| 98 | const VAddr nrr_addr{rp.Pop<VAddr>()}; | 104 | |
| 99 | const u64 nrr_size{rp.Pop<u64>()}; | 105 | LOG_DEBUG(Service_LDR, |
| 100 | LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr, | 106 | "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}", |
| 101 | nrr_size); | 107 | process_id, nrr_address, nrr_size); |
| 102 | 108 | ||
| 103 | if (!initialized) { | 109 | if (!initialized) { |
| 104 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 110 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| @@ -116,24 +122,26 @@ public: | |||
| 116 | } | 122 | } |
| 117 | 123 | ||
| 118 | // NRR Address does not fall on 0x1000 byte boundary | 124 | // NRR Address does not fall on 0x1000 byte boundary |
| 119 | if (!Common::Is4KBAligned(nrr_addr)) { | 125 | if (!Common::Is4KBAligned(nrr_address)) { |
| 120 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 126 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", |
| 127 | nrr_address); | ||
| 121 | IPC::ResponseBuilder rb{ctx, 2}; | 128 | IPC::ResponseBuilder rb{ctx, 2}; |
| 122 | rb.Push(ERROR_INVALID_ALIGNMENT); | 129 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 123 | return; | 130 | return; |
| 124 | } | 131 | } |
| 125 | 132 | ||
| 126 | // NRR Size is zero or causes overflow | 133 | // NRR Size is zero or causes overflow |
| 127 | if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { | 134 | if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 || |
| 135 | !Common::Is4KBAligned(nrr_size)) { | ||
| 128 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", | 136 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", |
| 129 | nrr_addr, nrr_size); | 137 | nrr_address, nrr_size); |
| 130 | IPC::ResponseBuilder rb{ctx, 2}; | 138 | IPC::ResponseBuilder rb{ctx, 2}; |
| 131 | rb.Push(ERROR_INVALID_SIZE); | 139 | rb.Push(ERROR_INVALID_SIZE); |
| 132 | return; | 140 | return; |
| 133 | } | 141 | } |
| 134 | // Read NRR data from memory | 142 | // Read NRR data from memory |
| 135 | std::vector<u8> nrr_data(nrr_size); | 143 | std::vector<u8> nrr_data(nrr_size); |
| 136 | Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); | 144 | Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); |
| 137 | NRRHeader header; | 145 | NRRHeader header; |
| 138 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); | 146 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); |
| 139 | 147 | ||
| @@ -174,7 +182,7 @@ public: | |||
| 174 | hashes.emplace_back(hash); | 182 | hashes.emplace_back(hash); |
| 175 | } | 183 | } |
| 176 | 184 | ||
| 177 | nrr.insert_or_assign(nrr_addr, std::move(hashes)); | 185 | nrr.insert_or_assign(nrr_address, std::move(hashes)); |
| 178 | 186 | ||
| 179 | IPC::ResponseBuilder rb{ctx, 2}; | 187 | IPC::ResponseBuilder rb{ctx, 2}; |
| 180 | rb.Push(RESULT_SUCCESS); | 188 | rb.Push(RESULT_SUCCESS); |
| @@ -188,23 +196,30 @@ public: | |||
| 188 | return; | 196 | return; |
| 189 | } | 197 | } |
| 190 | 198 | ||
| 199 | struct Parameters { | ||
| 200 | u64_le process_id; | ||
| 201 | u64_le nrr_address; | ||
| 202 | }; | ||
| 203 | |||
| 191 | IPC::RequestParser rp{ctx}; | 204 | IPC::RequestParser rp{ctx}; |
| 192 | rp.Skip(2, false); | 205 | const auto [process_id, nrr_address] = rp.PopRaw<Parameters>(); |
| 193 | const auto nrr_addr{rp.Pop<VAddr>()}; | 206 | |
| 194 | LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr); | 207 | LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id, |
| 208 | nrr_address); | ||
| 195 | 209 | ||
| 196 | if (!Common::Is4KBAligned(nrr_addr)) { | 210 | if (!Common::Is4KBAligned(nrr_address)) { |
| 197 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 211 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", |
| 212 | nrr_address); | ||
| 198 | IPC::ResponseBuilder rb{ctx, 2}; | 213 | IPC::ResponseBuilder rb{ctx, 2}; |
| 199 | rb.Push(ERROR_INVALID_ALIGNMENT); | 214 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 200 | return; | 215 | return; |
| 201 | } | 216 | } |
| 202 | 217 | ||
| 203 | const auto iter = nrr.find(nrr_addr); | 218 | const auto iter = nrr.find(nrr_address); |
| 204 | if (iter == nrr.end()) { | 219 | if (iter == nrr.end()) { |
| 205 | LOG_ERROR(Service_LDR, | 220 | LOG_ERROR(Service_LDR, |
| 206 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", | 221 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", |
| 207 | nrr_addr); | 222 | nrr_address); |
| 208 | IPC::ResponseBuilder rb{ctx, 2}; | 223 | IPC::ResponseBuilder rb{ctx, 2}; |
| 209 | rb.Push(ERROR_INVALID_NRR_ADDRESS); | 224 | rb.Push(ERROR_INVALID_NRR_ADDRESS); |
| 210 | return; | 225 | return; |
| @@ -216,16 +231,22 @@ public: | |||
| 216 | } | 231 | } |
| 217 | 232 | ||
| 218 | void LoadNro(Kernel::HLERequestContext& ctx) { | 233 | void LoadNro(Kernel::HLERequestContext& ctx) { |
| 234 | struct Parameters { | ||
| 235 | u64_le process_id; | ||
| 236 | u64_le image_address; | ||
| 237 | u64_le image_size; | ||
| 238 | u64_le bss_address; | ||
| 239 | u64_le bss_size; | ||
| 240 | }; | ||
| 241 | |||
| 219 | IPC::RequestParser rp{ctx}; | 242 | IPC::RequestParser rp{ctx}; |
| 220 | rp.Skip(2, false); | 243 | const auto [process_id, nro_address, nro_size, bss_address, bss_size] = |
| 221 | const VAddr nro_addr{rp.Pop<VAddr>()}; | 244 | rp.PopRaw<Parameters>(); |
| 222 | const u64 nro_size{rp.Pop<u64>()}; | 245 | |
| 223 | const VAddr bss_addr{rp.Pop<VAddr>()}; | 246 | LOG_DEBUG(Service_LDR, |
| 224 | const u64 bss_size{rp.Pop<u64>()}; | 247 | "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, " |
| 225 | LOG_DEBUG( | 248 | "bss_size={:016X}", |
| 226 | Service_LDR, | 249 | process_id, nro_address, nro_size, bss_address, bss_size); |
| 227 | "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}", | ||
| 228 | nro_addr, nro_size, bss_addr, bss_size); | ||
| 229 | 250 | ||
| 230 | if (!initialized) { | 251 | if (!initialized) { |
| 231 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 252 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| @@ -243,8 +264,9 @@ public: | |||
| 243 | } | 264 | } |
| 244 | 265 | ||
| 245 | // NRO Address does not fall on 0x1000 byte boundary | 266 | // NRO Address does not fall on 0x1000 byte boundary |
| 246 | if (!Common::Is4KBAligned(nro_addr)) { | 267 | if (!Common::Is4KBAligned(nro_address)) { |
| 247 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); | 268 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", |
| 269 | nro_address); | ||
| 248 | IPC::ResponseBuilder rb{ctx, 2}; | 270 | IPC::ResponseBuilder rb{ctx, 2}; |
| 249 | rb.Push(ERROR_INVALID_ALIGNMENT); | 271 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 250 | return; | 272 | return; |
| @@ -252,15 +274,15 @@ public: | |||
| 252 | 274 | ||
| 253 | // NRO Size or BSS Size is zero or causes overflow | 275 | // NRO Size or BSS Size is zero or causes overflow |
| 254 | const auto nro_size_valid = | 276 | const auto nro_size_valid = |
| 255 | nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); | 277 | nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size); |
| 256 | const auto bss_size_valid = | 278 | const auto bss_size_valid = nro_size + bss_size >= nro_size && |
| 257 | nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); | 279 | (bss_size == 0 || bss_address + bss_size > bss_address); |
| 258 | 280 | ||
| 259 | if (!nro_size_valid || !bss_size_valid) { | 281 | if (!nro_size_valid || !bss_size_valid) { |
| 260 | LOG_ERROR(Service_LDR, | 282 | LOG_ERROR(Service_LDR, |
| 261 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " | 283 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " |
| 262 | "bss_address={:016X}, bss_size={:016X})", | 284 | "bss_address={:016X}, bss_size={:016X})", |
| 263 | nro_addr, nro_size, bss_addr, bss_size); | 285 | nro_address, nro_size, bss_address, bss_size); |
| 264 | IPC::ResponseBuilder rb{ctx, 2}; | 286 | IPC::ResponseBuilder rb{ctx, 2}; |
| 265 | rb.Push(ERROR_INVALID_SIZE); | 287 | rb.Push(ERROR_INVALID_SIZE); |
| 266 | return; | 288 | return; |
| @@ -268,7 +290,7 @@ public: | |||
| 268 | 290 | ||
| 269 | // Read NRO data from memory | 291 | // Read NRO data from memory |
| 270 | std::vector<u8> nro_data(nro_size); | 292 | std::vector<u8> nro_data(nro_size); |
| 271 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | 293 | Memory::ReadBlock(nro_address, nro_data.data(), nro_size); |
| 272 | 294 | ||
| 273 | SHA256Hash hash{}; | 295 | SHA256Hash hash{}; |
| 274 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); | 296 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); |
| @@ -318,17 +340,18 @@ public: | |||
| 318 | return; | 340 | return; |
| 319 | } | 341 | } |
| 320 | 342 | ||
| 321 | ASSERT(vm_manager | 343 | ASSERT( |
| 322 | .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode) | 344 | vm_manager |
| 323 | .IsSuccess()); | 345 | .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) |
| 324 | ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); | 346 | .IsSuccess()); |
| 347 | ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); | ||
| 325 | 348 | ||
| 326 | if (bss_size > 0) { | 349 | if (bss_size > 0) { |
| 327 | ASSERT(vm_manager | 350 | ASSERT(vm_manager |
| 328 | .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, | 351 | .MirrorMemory(*map_address + nro_size, bss_address, bss_size, |
| 329 | Kernel::MemoryState::ModuleCode) | 352 | Kernel::MemoryState::ModuleCode) |
| 330 | .IsSuccess()); | 353 | .IsSuccess()); |
| 331 | ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); | 354 | ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); |
| 332 | } | 355 | } |
| 333 | 356 | ||
| 334 | vm_manager.ReprotectRange(*map_address, header.text_size, | 357 | vm_manager.ReprotectRange(*map_address, header.text_size, |
| @@ -348,13 +371,6 @@ public: | |||
| 348 | } | 371 | } |
| 349 | 372 | ||
| 350 | void UnloadNro(Kernel::HLERequestContext& ctx) { | 373 | void UnloadNro(Kernel::HLERequestContext& ctx) { |
| 351 | IPC::RequestParser rp{ctx}; | ||
| 352 | rp.Skip(2, false); | ||
| 353 | const VAddr mapped_addr{rp.PopRaw<VAddr>()}; | ||
| 354 | const VAddr heap_addr{rp.PopRaw<VAddr>()}; | ||
| 355 | LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr, | ||
| 356 | heap_addr); | ||
| 357 | |||
| 358 | if (!initialized) { | 374 | if (!initialized) { |
| 359 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 375 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| 360 | IPC::ResponseBuilder rb{ctx, 2}; | 376 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -362,22 +378,30 @@ public: | |||
| 362 | return; | 378 | return; |
| 363 | } | 379 | } |
| 364 | 380 | ||
| 365 | if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { | 381 | struct Parameters { |
| 366 | LOG_ERROR(Service_LDR, | 382 | u64_le process_id; |
| 367 | "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " | 383 | u64_le nro_address; |
| 368 | "bss_addr={:016X})!", | 384 | }; |
| 369 | mapped_addr, heap_addr); | 385 | |
| 386 | IPC::RequestParser rp{ctx}; | ||
| 387 | const auto [process_id, nro_address] = rp.PopRaw<Parameters>(); | ||
| 388 | LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id, | ||
| 389 | nro_address); | ||
| 390 | |||
| 391 | if (!Common::Is4KBAligned(nro_address)) { | ||
| 392 | LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})", | ||
| 393 | nro_address); | ||
| 370 | IPC::ResponseBuilder rb{ctx, 2}; | 394 | IPC::ResponseBuilder rb{ctx, 2}; |
| 371 | rb.Push(ERROR_INVALID_ALIGNMENT); | 395 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 372 | return; | 396 | return; |
| 373 | } | 397 | } |
| 374 | 398 | ||
| 375 | const auto iter = nro.find(mapped_addr); | 399 | const auto iter = nro.find(nro_address); |
| 376 | if (iter == nro.end()) { | 400 | if (iter == nro.end()) { |
| 377 | LOG_ERROR(Service_LDR, | 401 | LOG_ERROR(Service_LDR, |
| 378 | "The NRO attempting to unmap was not mapped or has an invalid address " | 402 | "The NRO attempting to be unmapped was not mapped or has an invalid address " |
| 379 | "(actual {:016X})!", | 403 | "(nro_address=0x{:016X})!", |
| 380 | mapped_addr); | 404 | nro_address); |
| 381 | IPC::ResponseBuilder rb{ctx, 2}; | 405 | IPC::ResponseBuilder rb{ctx, 2}; |
| 382 | rb.Push(ERROR_INVALID_NRO_ADDRESS); | 406 | rb.Push(ERROR_INVALID_NRO_ADDRESS); |
| 383 | return; | 407 | return; |
| @@ -386,10 +410,7 @@ public: | |||
| 386 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 410 | auto& vm_manager = Core::CurrentProcess()->VMManager(); |
| 387 | const auto& nro_size = iter->second.size; | 411 | const auto& nro_size = iter->second.size; |
| 388 | 412 | ||
| 389 | ASSERT(vm_manager | 413 | ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); |
| 390 | .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode) | ||
| 391 | .IsSuccess()); | ||
| 392 | ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); | ||
| 393 | 414 | ||
| 394 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 415 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); |
| 395 | 416 | ||
| @@ -459,11 +480,10 @@ private: | |||
| 459 | std::map<VAddr, NROInfo> nro; | 480 | std::map<VAddr, NROInfo> nro; |
| 460 | std::map<VAddr, std::vector<SHA256Hash>> nrr; | 481 | std::map<VAddr, std::vector<SHA256Hash>> nrr; |
| 461 | 482 | ||
| 462 | bool IsValidNROHash(const SHA256Hash& hash) { | 483 | bool IsValidNROHash(const SHA256Hash& hash) const { |
| 463 | return std::any_of( | 484 | return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { |
| 464 | nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { | 485 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); |
| 465 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); | 486 | }); |
| 466 | }); | ||
| 467 | } | 487 | } |
| 468 | 488 | ||
| 469 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 489 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |