orbis: propagate SIGSEGV, SIGBUS and SIGFPE in userspace to guest handler

fix minor race
fix signal delivery without handler freeze
This commit is contained in:
DH 2025-09-21 09:13:21 +03:00
parent 70fa577a7b
commit f7651d7eae
4 changed files with 96 additions and 39 deletions

View file

@ -325,14 +325,14 @@ void Thread::sendSignal(int signo) {
}
}
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
perror("pthread_sigqueue");
}
// TODO: suspend uses another delivery confirmation
if (signo != -1) {
interruptedMtx.store(1, std::memory_order::release);
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
perror("pthread_sigqueue");
}
while (interruptedMtx.wait(1, std::chrono::microseconds(1000)) !=
std::errc{}) {
if (interruptedMtx.load() == 0) {
@ -343,6 +343,10 @@ void Thread::sendSignal(int signo) {
perror("pthread_sigqueue");
}
}
} else {
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
perror("pthread_sigqueue");
}
}
}

View file

@ -8,6 +8,7 @@
#include "ipmi.hpp"
#include "linker.hpp"
#include "ops.hpp"
#include "orbis/ucontext.hpp"
#include "orbis/utils/Logs.hpp"
#include "rx/Config.hpp"
#include "rx/mem.hpp"
@ -16,6 +17,7 @@
#include "vfs.hpp"
#include "vm.hpp"
#include "xbyak/xbyak.h"
#include <optional>
#include <orbis/utils/Rc.hpp>
#include <print>
#include <rx/Version.hpp>
@ -170,6 +172,37 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) {
} else {
rx::printStackTrace(reinterpret_cast<ucontext_t *>(ucontext), 2);
}
if (orbis::g_currentThread != nullptr) {
auto toGuestSigno = [](int sig) -> std::optional<orbis::Signal> {
switch (sig) {
case SIGSEGV:
return orbis::kSigSegv;
case SIGBUS:
return orbis::kSigBus;
case SIGFPE:
return orbis::kSigFpe;
default:
return std::nullopt;
}
};
if (auto guestSigno = toGuestSigno(sig)) {
auto context = reinterpret_cast<ucontext_t *>(ucontext);
bool inGuestCode =
context->uc_mcontext.gregs[REG_RIP] < orbis::kMaxAddress;
if (inGuestCode) {
if (rx::thread::invokeSignalHandler(orbis::g_currentThread,
*guestSigno, context)) {
return;
}
}
}
}
}
struct sigaction act{};

View file

@ -129,42 +129,16 @@ handleSigUser(int sig, siginfo_t *info, void *ucontext) {
} else if (guestSignal >= 0) {
ORBIS_LOG_WARNING(__FUNCTION__, "handled signal", guestSignal, inGuestCode,
::getpid(), thread->tid);
auto it = thread->tproc->sigActions.find(guestSignal);
if (it != thread->tproc->sigActions.end()) {
auto guestContext = reinterpret_cast<ucontext_t *>(
inGuestCode ? context : thread->context);
if (!rx::thread::invokeSignalHandler(thread, guestSignal,
inGuestCode ? context : nullptr)) {
// no handler, mark signal as delivered
std::uint32_t prevValue = 1;
if (thread->interruptedMtx.compare_exchange_strong(prevValue, 0)) {
thread->interruptedMtx.notify_one();
}
auto sigact = it->second;
thread->setSigMask(sigact.mask);
auto handlerPtr = reinterpret_cast<std::uintptr_t>(sigact.handler);
auto rsp = guestContext->uc_mcontext.gregs[REG_RSP];
ORBIS_LOG_WARNING(__FUNCTION__, "invoking signal handler", guestSignal,
rsp, thread->stackStart, thread->stackEnd);
// FIXME: alt stack?
rsp -= 128; // redzone
rsp -= sizeof(orbis::SigFrame);
// FIXME handle flags
auto &sigFrame = *std::bit_cast<orbis::SigFrame *>(rsp);
sigFrame = {};
rx::thread::copyContext(thread, sigFrame.context, *guestContext);
sigFrame.info.signo = guestSignal;
sigFrame.handler = handlerPtr;
guestContext->uc_mcontext.gregs[REG_RDI] = guestSignal; // arg1, signo
guestContext->uc_mcontext.gregs[REG_RSI] =
std::bit_cast<std::uintptr_t>(&sigFrame.info); // arg2, siginfo
guestContext->uc_mcontext.gregs[REG_RDX] =
std::bit_cast<std::uintptr_t>(&sigFrame.context); // arg3, ucontext
guestContext->uc_mcontext.gregs[REG_RCX] = 0; // arg4, si_addr
guestContext->uc_mcontext.gregs[REG_RIP] = handlerPtr;
guestContext->uc_mcontext.gregs[REG_RSP] = rx::alignDown(rsp, 16);
guestSignal = -1;
}
}
@ -186,6 +160,50 @@ std::size_t rx::thread::getSigAltStackSize() {
return sigStackSize;
}
bool rx::thread::invokeSignalHandler(orbis::Thread *thread, int guestSignal,
ucontext_t *context) {
auto it = thread->tproc->sigActions.find(guestSignal);
if (it == thread->tproc->sigActions.end()) {
return false;
}
auto guestContext =
reinterpret_cast<ucontext_t *>(context ? context : thread->context);
auto sigact = it->second;
thread->setSigMask(sigact.mask);
auto handlerPtr = reinterpret_cast<std::uintptr_t>(sigact.handler);
auto rsp = guestContext->uc_mcontext.gregs[REG_RSP];
ORBIS_LOG_WARNING(__FUNCTION__, "invoking signal handler", guestSignal, rsp,
thread->stackStart, thread->stackEnd);
// FIXME: alt stack?
rsp -= 128; // redzone
rsp -= sizeof(orbis::SigFrame);
// FIXME handle flags
auto &sigFrame = *std::bit_cast<orbis::SigFrame *>(rsp);
sigFrame = {};
rx::thread::copyContext(thread, sigFrame.context, *guestContext);
sigFrame.info.signo = guestSignal;
sigFrame.handler = handlerPtr;
guestContext->uc_mcontext.gregs[REG_RDI] = guestSignal; // arg1, signo
guestContext->uc_mcontext.gregs[REG_RSI] =
std::bit_cast<std::uintptr_t>(&sigFrame.info); // arg2, siginfo
guestContext->uc_mcontext.gregs[REG_RDX] =
std::bit_cast<std::uintptr_t>(&sigFrame.context); // arg3, ucontext
guestContext->uc_mcontext.gregs[REG_RCX] = 0; // arg4, si_addr
guestContext->uc_mcontext.gregs[REG_RIP] = handlerPtr;
guestContext->uc_mcontext.gregs[REG_RSP] = rx::alignDown(rsp, 16);
return true;
}
void rx::thread::copyContext(orbis::MContext &dst, const mcontext_t &src) {
// dst.onstack = src.gregs[REG_ONSTACK];
dst.rdi = src.gregs[REG_RDI];

View file

@ -13,6 +13,8 @@ void setupThisThread();
void copyContext(orbis::MContext &dst, const mcontext_t &src);
void copyContext(orbis::Thread *thread, orbis::UContext &dst,
const ucontext_t &src);
bool invokeSignalHandler(orbis::Thread *thread, int signo,
ucontext_t *context = nullptr);
void setContext(orbis::Thread *thread, const orbis::UContext &src);
void invoke(orbis::Thread *thread);
} // namespace rx::thread