From 3633346d53ec238580dcfd06704961558729a58b Mon Sep 17 00:00:00 2001 From: Pascal Zittlau Date: Tue, 16 Dec 2025 11:14:10 +0100 Subject: [PATCH] support rt_sigreturn --- docs/TODO.md | 4 ++-- src/main.zig | 13 +++++++++++++ src/syscalls.zig | 18 +++++++++++++++++- src/test/signal_handler.zig | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/test/signal_handler.zig diff --git a/docs/TODO.md b/docs/TODO.md index 6f8872c..58b292f 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -15,8 +15,8 @@ IvyBridge(2012) and AMD Zen 2 Family 17H(2019) and Linux 5.9(2020). - [x] `clone`: with and without stack switching - [x] `clone3`: with and without stack switching - [x] `fork`: likely there is nothing to be done here but just to be sure, check again -- [ ] `sigretun`/`rt_sigreturn`: we can't use the normal `syscall` interception because we push - something onto the stack, so `ucontext` isn't on top anymore. +- [x] `rt_sigreturn`: we can't use the normal `syscall` interception because we push something onto + the stack, so `ucontext` isn't on top anymore. - [x] `/proc/self/exe`: intercept calls to `readlink`/`readlinkat` with that as argument - [ ] `auxv`: check if that is setup correctly and completely - [ ] JIT support: intercept `mmap`, `mprotect` and `mremap` that change pages to be executable diff --git a/src/main.zig b/src/main.zig index f9e0b2c..13823b4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -376,6 +376,19 @@ test "nolibc_pie_fork" { // ); // } +test "nolibc_nopie_signal_handler" { + try testHelper( + &.{ flicker_path, getTestExePath("nolibc_nopie_signal_handler") }, + "In signal handler\nSignal handled successfully\n", + ); +} +test "nolibc_pie_signal_handler" { + try testHelper( + &.{ flicker_path, getTestExePath("nolibc_pie_signal_handler") }, + "In signal handler\nSignal handled successfully\n", + ); +} + fn testPrintArgs(comptime name: []const u8) !void { const exe_path = getTestExePath(name); const loader_argv: []const []const u8 = &.{ flicker_path, exe_path, "foo", "bar", "baz hi" }; diff --git a/src/syscalls.zig b/src/syscalls.zig index dad0726..f64e03f 100644 --- a/src/syscalls.zig +++ b/src/syscalls.zig @@ -64,7 +64,23 @@ export fn syscall_handler(ctx: *SavedContext) callconv(.c) void { return; }, .rt_sigreturn => { - @panic("sigreturn is not supported yet"); + // The kernel expects the stack pointer to point to the `ucontext` structure. But in our + // case `syscallEntry` pushed the `SavedContext` onto the stack. + // So we just need to reset the stack pointer to what it was before `syscallEntry` was + // called. The `SavedContext` includes the return address pushed by the trampoline, so + // the original stack pointer is exactly at the end of `SavedContext`. + const rsp_orig = @intFromPtr(ctx) + @sizeOf(SavedContext); + + asm volatile ( + \\ mov %[rsp], %%rsp + \\ syscall + \\ ud2 + : + : [rsp] "r" (rsp_orig), + [number] "{rax}" (ctx.rax), + : .{ .memory = true } + ); + unreachable; }, .execve, .execveat => |s| { // TODO: option to persist across new processes diff --git a/src/test/signal_handler.zig b/src/test/signal_handler.zig new file mode 100644 index 0000000..508c5b6 --- /dev/null +++ b/src/test/signal_handler.zig @@ -0,0 +1,35 @@ +const std = @import("std"); +const linux = std.os.linux; + +var handled = false; + +fn handler(sig: i32, _: *const linux.siginfo_t, _: ?*anyopaque) callconv(.c) void { + if (sig == linux.SIG.USR1) { + handled = true; + const msg = "In signal handler\n"; + _ = linux.syscall3(.write, 1, @intFromPtr(msg.ptr), msg.len); + } +} + +pub fn main() !void { + const act = linux.Sigaction{ + .handler = .{ .sigaction = handler }, + .mask = std.mem.zeroes(linux.sigset_t), + .flags = linux.SA.SIGINFO | linux.SA.RESTART, + }; + + if (linux.sigaction(linux.SIG.USR1, &act, null) != 0) { + return error.SigactionFailed; + } + + _ = linux.kill(linux.getpid(), linux.SIG.USR1); + + if (handled) { + const msg = "Signal handled successfully\n"; + _ = linux.syscall3(.write, 1, @intFromPtr(msg.ptr), msg.len); + } else { + const msg = "Signal NOT handled\n"; + _ = linux.syscall3(.write, 1, @intFromPtr(msg.ptr), msg.len); + std.process.exit(1); + } +}