diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 9da2728d50..9bc366006b 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -1973,6 +1973,11 @@ if(BUILD_TESTING) add_test(NAME guest_raise_in_system_call COMMAND guest_raise_in_system_call_tests) set_tests_properties(guest_raise_in_system_call PROPERTIES TIMEOUT 30 ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + foreach(delay IN ITEMS raise return) + add_test(NAME guest_raise_in_system_call_delay_${delay} COMMAND guest_raise_in_system_call_tests --delay-${delay}) + set_tests_properties(guest_raise_in_system_call_delay_${delay} PROPERTIES TIMEOUT 30 ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endforeach() add_test_executable(guest_raise_in_host_code_tests ${LIBS_INCLUDE_DIR}/tests/GuestRaiseInHostCode.cpp) target_include_directories(guest_raise_in_host_code_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_raise_in_host_code_tests PRIVATE libkernel libc) diff --git a/core/libs/tests/GuestRaiseInSystemCall.cpp b/core/libs/tests/GuestRaiseInSystemCall.cpp index 999f4b5755..6f0d08c8be 100644 --- a/core/libs/tests/GuestRaiseInSystemCall.cpp +++ b/core/libs/tests/GuestRaiseInSystemCall.cpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -15,6 +17,8 @@ static constexpr int SigUsr1 = 30; static constexpr int Raises = 10; static constexpr DWORD PipeBytes = 4096; static constexpr std::uint64_t BlockBytes = 4u * PipeBytes; +static constexpr std::size_t RipOffset = 0xe0; +static constexpr ULONG ThreadSuspendCount = 35; static void Require(bool value) { if (!value) std::abort(); } static std::FILE* shared = nullptr; @@ -22,18 +26,43 @@ static std::atomic deliveries{0}; static std::atomic released{0}; static std::atomic written{0}; static std::atomic blocked{0}; -static std::atomic raising{0}; +static std::atomic writerThread{nullptr}; +static bool delayReturn = false; -static void APS5_VABI Handler(int, void*) { +static bool InTestImage(std::uint64_t rip) { + const auto base = reinterpret_cast(GetModuleHandleW(nullptr)); + const auto* dos = reinterpret_cast(base); + const auto* nt = reinterpret_cast(base + dos->e_lfanew); + return rip >= base && rip - base < nt->OptionalHeader.SizeOfImage; +} + +static bool Suspended(HANDLE thread) { + using Query = LONG(NTAPI*)(HANDLE, ULONG, void*, ULONG, ULONG*); + static const auto query = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationThread")); + ULONG count = 0; + Require(query != nullptr && query(thread, ThreadSuspendCount, &count, sizeof(count), nullptr) >= 0); + return count > 0; +} + +static void APS5_VABI Handler(int, void* context) { const auto delivery = deliveries.fetch_add(1) + 1; - Require(written.load() == delivery); + std::uint64_t rip = 0; + std::memcpy(&rip, static_cast(context) + RipOffset, sizeof(rip)); + Require(InTestImage(rip)); while (released.load() < delivery) std::this_thread::yield(); } static void* APS5_VABI Writer(void*) { + HANDLE self = nullptr; + Require(DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &self, THREAD_QUERY_INFORMATION, FALSE, 0) != 0); + writerThread.store(self); const std::string block(BlockBytes, 'x'); for (int raise = 1; raise <= Raises; ++raise) { Require(std::fwrite(block.data(), 1, block.size(), shared) == block.size()); + if (delayReturn) { + volatile std::uint64_t spin = 0; + for (std::uint64_t i = 0; i < 100000000; ++i) spin = spin + i; + } written.store(raise); while (deliveries.load() < raise) { } @@ -41,7 +70,10 @@ static void* APS5_VABI Writer(void*) { return nullptr; } -int main() { +int main(int argc, char** argv) { + const std::string_view mode = argc > 1 ? argv[1] : ""; + Require(mode.empty() || mode == "--delay-raise" || mode == "--delay-return"); + delayReturn = mode == "--delay-return"; std::atomic done{false}; std::thread watchdog([&done] { const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(25); @@ -62,7 +94,8 @@ int main() { total += got; if (block == Raises || total <= static_cast(block) * (BlockBytes + 1)) continue; blocked.store(++block); - while (raising.load() < block) std::this_thread::yield(); + while (!Suspended(writerThread.load())) { + } std::this_thread::sleep_for(std::chrono::milliseconds(100)); Require(written.load() < block); } @@ -72,7 +105,7 @@ int main() { Require(scePthreadCreate(&writer, nullptr, Writer, nullptr, "pipe writer") == 0); for (int raise = 1; raise <= Raises; ++raise) { while (blocked.load() < raise) std::this_thread::yield(); - raising.store(raise); + if (mode == "--delay-raise") std::this_thread::sleep_for(std::chrono::milliseconds(200)); Require(sceKernelRaiseException(writer, SigUsr1) == 0); while (deliveries.load() < raise) std::this_thread::yield(); Require(std::fputc('y', shared) == 'y'); @@ -83,6 +116,7 @@ int main() { std::fclose(shared); reader.join(); CloseHandle(read); + CloseHandle(writerThread.load()); done.store(true); watchdog.join(); }