diff --git a/24-syscall/usyscall.c b/24-syscall/usyscall.c
index c74e5d8..bd4f1f2 100644
--- a/24-syscall/usyscall.c
+++ b/24-syscall/usyscall.c
@@ -33,8 +33,24 @@ volatile char usyscall_flag = SYSCALL_DISPATCH_FILTER_ALLOW;
// Enable user space system call dispatching, but exclude the region
// from [offset, offset+length].
void usyscall_init(void *offset, ssize_t length) {
- // FIXME: install SIGSYS signal handler with SA_SIGINFO
- // FIXME: prctl(2) and PR_SET_SYSCALL_USER_DISPATCH
+ // Install our own system call handler and request the SIGINFO
+ // variant, which we need to get all necessary information to
+ // reinject the system call.
+ struct sigaction sa = {0};
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = &usyscall_signal;
+
+ if (sigaction(SIGSYS, &sa, NULL) != 0)
+ die("sigaction");
+
+ // Just enable the user space system call dispatcher.
+ if (prctl(PR_SET_SYSCALL_USER_DISPATCH,
+ PR_SYS_DISPATCH_ON,
+ offset, length, &usyscall_flag) < 0) {
+ fprintf(stderr, "kernel too old? (requires at least 5.11)\n");
+ die("prctl");
+ }
}
// Just a wrapper function to enable the usyscall mechanism
@@ -57,10 +73,50 @@ void usyscall_signal(int signum, siginfo_t *info, void *context) {
ctx->uc_mcontext.gregs[REG_R8]
}; (void) args;
- // HINT: Return address can be obtained with:
- // __builtin_extract_return_addr(__builtin_return_address (0))
- // FIXME: call usyscall_init(return_address, 20) a second time
- // FIXME: Interpret some system calls (e.g., __NR_write)
+ // We have the problem that returning from a signal requires a
+ // system call (rt_sigreturn). However, we want to return with an
+ // enabled usyscall dispatcher. Therefore, we have to allow
+ // syscalls in a small area in the glibc.
+ static bool return_rt_allowed = false;
+ if (!return_rt_allowed) { // Executed exactly once
+ return_rt_allowed = true;
+
+ // We will return to a stub function in the glibc that issues
+ // the sigreturn system call. We use a compiler instrinstic to get our return address
+ void *__return_rt = __builtin_extract_return_addr(__builtin_return_address (0));
+
+ // Reinitialize the usyscall mechanism
+ usyscall_init(__return_rt, 20);
+ }
+
+ // Special treatment for some system calls
+ if (info->si_syscall == __NR_write) {
+ if (args[0] == STDOUT_FILENO) {
+ // For write to stdout, we use rot13 to encrypt the
+ // output. We copy the source buffer here (and issue
+ // multiple writes in case) to avoid modifying the source buffer
+ char *orig = (char*) args[1];
+ size_t count = args[2];
+ char buf[64];
+ for (unsigned i = 0; i < count; i++) {
+ buf[i % sizeof(buf)] = rot13(orig[i]);
+ if (i == (sizeof(buf) - 1))
+ write(1, buf, 64);
+ }
+ write(1, buf, count % sizeof(buf));
+ }
+ } else if (info->si_syscall == 512) {
+ // We also introduce a new system call with the number 512
+ printf("MySyscall: 0x%lx\n", args[0]);
+ } else {
+ // For all other system calls, we just execute the actual system call.
+ int rax = syscall(info->si_syscall, args[0], args[1], args[2], args[3], args[4], args[5]);
+ ctx->uc_mcontext.gregs[REG_RAX] = rax;
+ }
+
+ // Block system calls again
+ usyscall_enable(true);
+
// A return calls the rt_sigreturn system call. This has to be
// allowed here as the (offset+length) of prctl. length=20 bytes