AnyPS5/core/libs/tests/GuestNamedShmDescriptorReus...

178 lines
5.8 KiB
C++

#include "SceTypes.hpp"
#include "nid/Sha1.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cerrno>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <stdexcept>
#include <string>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <vector>
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<ssize_t>(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<std::uint8_t>(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<long>(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<int>(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<char*>(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<char*>(static_cast<std::uintptr_t>(-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;
}
}