Files
AnyPS5/core/libs/tests/GuestSignalHandlerMask.cpp
T
Yunus Shaikh 780aa44726 fix(libkernel): block every word of a signal handler's mask while it runs
main now keeps all four words of the guest signal mask. A handler's sa_mask above signal 32 is added too, and SIGKILL and SIGSTOP stay unblocked, as sigprocmask leaves them.
2026-10-21 15:52:34 -07:00

165 lines
4.9 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
#include <thread>
extern "C" {
int APS5_VABI sigaction_nid_postfix(int, const void*, void*);
int APS5_VABI sigprocmask_nid_postfix(int, const void*, void*);
int APS5_VABI raise_nid_postfix(int);
[[gnu::returns_twice]] int APS5_VABI setjmp_nid_postfix(std::uint64_t* buffer);
[[noreturn]] void APS5_VABI longjmp_nid_postfix(std::uint64_t* buffer, int value);
}
struct GuestSignalSet {
std::uint32_t bits[4];
};
struct GuestSigaction {
std::uintptr_t handler;
int flags;
GuestSignalSet mask;
};
static constexpr int SigKill = 9;
static constexpr int SigTerm = 15;
static constexpr int SigUsr1 = 30;
static constexpr int SigUsr2 = 31;
static constexpr int SaRestart = 0x2;
static constexpr int SaResethand = 0x4;
static constexpr int SaNodefer = 0x10;
static constexpr int SigBlock = 1;
static constexpr int SigUnblock = 2;
static std::uint32_t Bit(int signal) { return 1u << (signal - 1); }
static void Require(bool value) { if (!value) std::abort(); }
static int calls = 0;
static int callsAfterNestedRaise = 0;
static std::uint32_t maskInHandler = 0;
static std::uint32_t CurrentMask() {
GuestSignalSet set{};
Require(sigprocmask_nid_postfix(SigBlock, nullptr, &set) == 0);
return set.bits[0];
}
static void APS5_VABI RaiseAgain(int signal) {
if (++calls == 1) {
maskInHandler = CurrentMask();
Require(raise_nid_postfix(signal) == 0);
callsAfterNestedRaise = calls;
}
}
static void APS5_VABI Count(int) {
++calls;
}
static std::uint64_t handlerExit[12];
static void APS5_VABI LeaveWithLongjmp(int) {
++calls;
longjmp_nid_postfix(handlerExit, 1);
}
static void APS5_VABI BlockFromOtherThread(int) {
++calls;
std::thread([] {
const GuestSignalSet set{{Bit(SigTerm), 0, 0, 0}};
Require(sigprocmask_nid_postfix(SigBlock, &set, nullptr) == 0);
}).join();
}
static GuestSignalSet setInHandler{};
static void APS5_VABI RecordMask(int) {
++calls;
Require(sigprocmask_nid_postfix(SigBlock, nullptr, &setInHandler) == 0);
}
static void Install(int signal, void (APS5_VABI* handler)(int), int flags, std::uint32_t mask) {
GuestSigaction action{reinterpret_cast<std::uintptr_t>(handler), flags, {{mask, 0, 0, 0}}};
Require(sigaction_nid_postfix(signal, &action, nullptr) == 0);
}
static void CheckDeferred(int signal) {
calls = 0;
callsAfterNestedRaise = 0;
Install(signal, RaiseAgain, SaRestart, Bit(SigUsr1));
Require(raise_nid_postfix(signal) == 0);
Require(callsAfterNestedRaise == 1);
Require(calls == 2);
Require(maskInHandler == (Bit(signal) | Bit(SigUsr1)));
Require(CurrentMask() == 0);
}
static void CheckNodefer() {
calls = 0;
callsAfterNestedRaise = 0;
Install(SigUsr2, RaiseAgain, SaRestart | SaNodefer, 0);
Require(raise_nid_postfix(SigUsr2) == 0);
Require(callsAfterNestedRaise == 2);
Require(maskInHandler == 0);
Require(CurrentMask() == 0);
}
static void CheckResethand() {
calls = 0;
Install(SigUsr1, Count, SaResethand, 0);
Require(raise_nid_postfix(SigUsr1) == 0);
Require(calls == 1);
GuestSigaction current{};
Require(sigaction_nid_postfix(SigUsr1, nullptr, &current) == 0);
Require(current.handler == 0);
Require((current.flags & SaResethand) != 0);
}
static void CheckOtherThreadKept() {
calls = 0;
Install(SigUsr1, BlockFromOtherThread, SaRestart, 0);
Require(raise_nid_postfix(SigUsr1) == 0);
Require(calls == 1);
Require(CurrentMask() == Bit(SigTerm));
const GuestSignalSet set{{Bit(SigTerm), 0, 0, 0}};
Require(sigprocmask_nid_postfix(SigUnblock, &set, nullptr) == 0);
Require(CurrentMask() == 0);
}
static void CheckUpperMaskWords() {
calls = 0;
const GuestSigaction action{reinterpret_cast<std::uintptr_t>(RecordMask), SaRestart, {{Bit(SigKill), 1u << 7, 0, 1u << 31}}};
Require(sigaction_nid_postfix(SigUsr1, &action, nullptr) == 0);
Require(raise_nid_postfix(SigUsr1) == 0);
Require(calls == 1);
Require(setInHandler.bits[0] == Bit(SigUsr1) && setInHandler.bits[1] == 1u << 7 && setInHandler.bits[2] == 0 && setInHandler.bits[3] == 1u << 31);
GuestSignalSet after{};
Require(sigprocmask_nid_postfix(SigBlock, nullptr, &after) == 0);
Require(after.bits[0] == 0 && after.bits[1] == 0 && after.bits[2] == 0 && after.bits[3] == 0);
}
static void CheckLeftWithLongjmp() {
calls = 0;
Install(SigUsr1, LeaveWithLongjmp, SaRestart, Bit(SigUsr2));
for (int round = 1; round <= 2; ++round) {
if (setjmp_nid_postfix(handlerExit) == 0) {
raise_nid_postfix(SigUsr1);
std::abort();
}
Require(calls == round);
Require(CurrentMask() == 0);
}
}
int main() {
CheckDeferred(SigUsr2);
CheckDeferred(SigTerm);
CheckNodefer();
CheckResethand();
CheckOtherThreadKept();
CheckUpperMaskWords();
CheckLeftWithLongjmp();
return 0;
}