#include "SceTypes.hpp" #include "nid/Sha1.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { int APS5_VABI shm_open_nid_postfix(const char*, int, int); int APS5_VABI close_nid_postfix(int); int APS5_VABI ftruncate_nid_postfix(int, std::int64_t); std::int64_t APS5_VABI pread_nid_postfix(int, void*, std::size_t, std::int64_t); std::int64_t APS5_VABI pwrite_nid_disambig1_nid_postfix(int, const void*, std::size_t, std::int64_t); void* APS5_VABI mmap_nid_postfix(void*, std::size_t, int, int, int, std::int64_t) noexcept; int APS5_VABI munmap_nid_postfix(void*, std::size_t) noexcept; int* APS5_VABI __error_nid_postfix(); } namespace { constexpr int ReadWrite = 2; constexpr int Create = 0x200; constexpr int Exclusive = 0x800; constexpr int Shared = 0x1; constexpr std::size_t Page = 0x4000; int armedDescriptor = -1; int replacementDescriptor = -1; bool armed = false; bool swapped = false; int CreateOrdinaryFile() { char path[] = "/tmp/anyps5-descriptor-reuse-XXXXXX"; const int descriptor = ::mkstemp(path); if (descriptor < 0) throw std::runtime_error("ordinary file creation failed"); if (::unlink(path) != 0) { ::close(descriptor); throw std::runtime_error("ordinary file unlink failed"); } constexpr char contents[] = "ordinary file contents"; if (::write(descriptor, contents, sizeof(contents)) != static_cast(sizeof(contents)) || ::lseek(descriptor, 0, SEEK_SET) != 0) { ::close(descriptor); throw std::runtime_error("ordinary file initialization failed"); } return descriptor; } void Require(bool condition, const char* message) { if (!condition) throw std::runtime_error(std::string(message) + " (guest errno=" + std::to_string(*__error_nid_postfix()) + ")"); } struct Object { std::string name = "/anyps5-descriptor-reuse-" + std::to_string(getpid()); std::string hostName = "/anyps5-shm-"; Object() { constexpr char hex[] = "0123456789abcdef"; const auto digest = Nid::Sha1(std::vector(name.begin(), name.end())); for (const auto byte : digest) { hostName += hex[byte >> 4]; hostName += hex[byte & 15]; } ::shm_unlink(hostName.c_str()); } ~Object() { ::shm_unlink(hostName.c_str()); } int Open(int flags) const { return shm_open_nid_postfix(name.c_str(), flags, 0600); } }; } extern "C" int fcntl(int descriptor, int command, ...) { long argument = 0; switch (command) { case F_GETFD: case F_GETFL: case F_GETOWN: #ifdef F_GETSIG case F_GETSIG: #endif #ifdef F_GETLEASE case F_GETLEASE: #endif #ifdef F_GETPIPE_SZ case F_GETPIPE_SZ: #endif #ifdef F_GET_SEALS case F_GET_SEALS: #endif break; #ifdef F_GETLK case F_GETLK: case F_SETLK: case F_SETLKW: { va_list args; va_start(args, command); argument = reinterpret_cast(va_arg(args, void*)); va_end(args); break; } #endif case F_DUPFD: case F_DUPFD_CLOEXEC: case F_SETFD: case F_SETFL: case F_SETOWN: #ifdef F_SETSIG case F_SETSIG: #endif #ifdef F_SETLEASE case F_SETLEASE: #endif #ifdef F_NOTIFY case F_NOTIFY: #endif #ifdef F_SETPIPE_SZ case F_SETPIPE_SZ: #endif #ifdef F_ADD_SEALS case F_ADD_SEALS: #endif { va_list args; va_start(args, command); argument = va_arg(args, int); va_end(args); break; } default: errno = EINVAL; return -1; } const long result = ::syscall(SYS_fcntl, descriptor, command, argument); if (command == F_GETFL && armed && !swapped) { swapped = true; if (::syscall(SYS_dup2, replacementDescriptor, armedDescriptor) != armedDescriptor) return -1; } return static_cast(result); } int main() { try { Object object; const int descriptor = object.Open(ReadWrite | Create | Exclusive); Require(descriptor >= 0 && ftruncate_nid_postfix(descriptor, Page) == 0, "shared-memory setup failed"); Require(pwrite_nid_disambig1_nid_postfix(descriptor, "original", 8, 0) == 8, "shared-memory seed failed"); const int verifier = object.Open(ReadWrite); Require(verifier >= 0, "shared-memory verifier open failed"); const int replacement = CreateOrdinaryFile(); Require(replacement >= 0, "ordinary-file setup failed"); armedDescriptor = descriptor; replacementDescriptor = replacement; armed = true; auto* mapped = static_cast(mmap_nid_postfix(nullptr, Page, 3, Shared, descriptor, 0)); armed = false; Require(swapped, "native F_GETFL interposer did not replace the guest descriptor"); Require(mapped != reinterpret_cast(static_cast(-1)), "guest mmap failed after descriptor replacement"); Require(std::memcmp(mapped, "original", 8) == 0, "mapping followed the replacement descriptor instead of the pinned shared-memory object"); mapped[0] = 0x70; char byte = 0; Require(pread_nid_postfix(verifier, &byte, 1, 0) == 1 && byte == 0x70, "mapping did not update the original shared-memory object"); Require(munmap_nid_postfix(mapped, Page) == 0, "guest mapping cleanup failed"); Require(close_nid_postfix(descriptor) == 0 && close_nid_postfix(verifier) == 0, "guest descriptor cleanup failed"); Require(::close(replacement) == 0, "replacement descriptor cleanup failed"); std::puts("named shared-memory descriptor reuse checks passed"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "%s\n", error.what()); return 1; } }