Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/libs/prx/libSceSystemService/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ target_include_directories(libSceSystemService PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceSystemService)

if(BUILD_TESTING)
if(WIN32)
add_test_executable(guest_system_service_path_boundary_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemServicePathBoundary.cpp)
target_include_directories(guest_system_service_path_boundary_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_system_service_path_boundary_tests PRIVATE libSceSystemService libc)
configure_windows_unwind(guest_system_service_path_boundary_tests)
add_test(NAME guest_system_service_path_boundary COMMAND guest_system_service_path_boundary_tests)
set_tests_properties(guest_system_service_path_boundary PROPERTIES TIMEOUT 20)
set_tests_properties(guest_system_service_path_boundary PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()

add_test_executable(guest_system_exit_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemExit.cpp)
target_include_directories(guest_system_exit_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_system_exit_tests PRIVATE libSceSystemService libc)
Expand Down
8 changes: 8 additions & 0 deletions core/libs/prx/libSceSystemService/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ std::string ConvertedExecutableName(const char* path) {
}
}

void RequireExecutableBeside(const std::filesystem::path& directory, const std::filesystem::path& target, const char* guestPath) {
if (target.lexically_normal().parent_path() != directory.lexically_normal()) {
UnconvertedExecutable(std::string("an executable outside /app0 (") + guestPath + ")");
}
}

#if defined(_WIN32)
std::wstring Utf16(const std::string& text) {
if (text.empty()) return {};
Expand Down Expand Up @@ -115,6 +121,7 @@ HostLaunch PrepareLaunch(const char* path, const char* const* arguments) {
launch.executable = mainLength > 0 && mainLength < main.size() ? main.substr(0, mainLength) : own;
} else {
const auto target = ownPath.parent_path() / (Utf16(ConvertedExecutableName(path)) + ownPath.extension().wstring());
RequireExecutableBeside(ownPath.parent_path(), target, path);
if (!std::filesystem::is_regular_file(target)) {
UnconvertedExecutable(std::string(path) + " without its converted executable; relink with --executable " + (path + std::size(ApplicationRoot) - 1));
}
Expand Down Expand Up @@ -156,6 +163,7 @@ HostLaunch PrepareLaunch(const char* path, const char* const* arguments) {
launch.executable = main != nullptr && *main != '\0' ? main : own.string();
} else {
const auto target = own.parent_path() / (ConvertedExecutableName(path) + own.extension().string());
RequireExecutableBeside(own.parent_path(), target, path);
if (!std::filesystem::is_regular_file(target)) {
UnconvertedExecutable(std::string(path) + " without its converted executable; relink with --executable " + (path + std::size(ApplicationRoot) - 1));
}
Expand Down
82 changes: 82 additions & 0 deletions core/libs/tests/GuestSystemServicePathBoundary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include "prx/libc/include/Shutdown.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <string>
#include <system_error>
#include <stdexcept>
#include <windows.h>

extern "C" int APS5_VABI sceSystemServiceLoadExec(const char*, const char* const*);

namespace {

void Require(bool value) {
if (!value) std::abort();
}

[[noreturn]] void UnexpectedLaunch() {
std::_Exit(86);
}

int Probe(const char* name) {
LibcRegisterShutdown_nid_postfix(UnexpectedLaunch);
const std::string path = std::string("/app0/..\\") + name;
try {
sceSystemServiceLoadExec(path.c_str(), nullptr);
} catch (const std::runtime_error& error) {
return std::string(error.what()).find(path) != std::string::npos ? 0 : 2;
}
return 1;
}

DWORD RunProbe(const std::filesystem::path& executable, const std::string& name) {
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
PROCESS_INFORMATION process{};
std::wstring command = L"\"" + executable.wstring() + L"\" --probe ";
command.append(name.begin(), name.end());
Require(CreateProcessW(executable.c_str(), command.data(), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &startup, &process) != 0);
const DWORD waitResult = WaitForSingleObject(process.hProcess, 10000);
if (waitResult != WAIT_OBJECT_0) {
TerminateProcess(process.hProcess, ERROR_TIMEOUT);
WaitForSingleObject(process.hProcess, 5000);
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
return ERROR_TIMEOUT;
}
DWORD code = 1;
if (!GetExitCodeProcess(process.hProcess, &code)) code = ERROR_GEN_FAILURE;
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
return code;
}

int Test() {
std::wstring executable(32768, L'\0');
const DWORD length = GetModuleFileNameW(nullptr, executable.data(), static_cast<DWORD>(executable.size()));
Require(length > 0 && length < executable.size());
executable.resize(length);
const std::filesystem::path own(executable);
const std::string name = "anyps5-path-boundary-" + std::to_string(GetCurrentProcessId()) + "-" + std::to_string(GetTickCount64());
const auto fixture = own.parent_path().parent_path() / (name + own.extension().string());
struct Cleanup {
std::filesystem::path path;
~Cleanup() {
std::error_code error;
std::filesystem::remove(path, error);
}
} cleanup{fixture};
std::filesystem::copy_file(own, fixture);
Require(RunProbe(own, name) == 0);
return 0;
}

}

int main(int argc, char** argv) {
if (argc == 3 && std::strcmp(argv[1], "--probe") == 0) return Probe(argv[2]);
Require(argc == 1);
return Test();
}
Loading