diff --git a/17-seccomp/seccomp.c b/17-seccomp/seccomp.c
index b1fc046..4d76038 100644
--- a/17-seccomp/seccomp.c
+++ b/17-seccomp/seccomp.c
@@ -41,20 +41,73 @@ typedef struct {
// Spawn a function within a seccomp-restricted child process
secure_func_t spawn_secure(void (*func)(void*, int), void* arg) {
- // FIXME: Create a pipe pair with pipe2(2)
- // FIXME: fork a child process
- // FIXME: In child, close all file descriptors besides the write end
- // FIXME: Invoke the given function and kill the child with syscall(__NR_exit, 0)
- secure_func_t p = {.pid = -1, .pipe = -1};
- return p;
+ // Create the child pipe.
+ // pipe[1] - the write end
+ // pipe[0] - the read end
+ int pipe[2];
+ if (pipe2(pipe, O_CLOEXEC))
+ die("pipe2");
+
+ pid_t pid = fork();
+ if (pid < 0) die("fork");
+ if (pid > 0) { // Parent
+ // Close the write end of the child pipe
+ close(pipe[1]);
+
+ // Create a handle for the called function
+ secure_func_t ret = {.pid = pid, .pipe = pipe[0] };
+ return ret;
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Child after here
+
+ // dup the pipe write end to the file descriptor 0
+ if (dup2(pipe[1], 0) < 0)
+ die("dup2");
+
+ // Close all file descriptors above 1 (requires Linux 5.9)
+ if (sys_close_range(1, ~0U, 0) == -1) {
+ die("close_range");
+ }
+
+ // Enter the strict seccomp mode. From here on, only read(2),
+ // write(2), _exit(2), and sigreturn(2) are allowed.
+ if (sys_seccomp(SECCOMP_SET_MODE_STRICT, 0, NULL) < 0)
+ die("seccomp");
+
+ // Execute the given function with the given argument and an
+ // output descriptor of 0 (see dup2 above)
+ func(arg, 0);
+
+ // We use the bare exit system call here to kill the child.
+ // Reason for this is, that the glibc calls exit_group(2),
+ // when we call _exit().
+ syscall(__NR_exit, 0);
+
+ // We place this unreachable here to avoid a compiler warning.
+ // This builtin does _not_ generate any code, but it only
+ // signals to the compiler, that we will never come here.
+ __builtin_unreachable();
}
// Complete a previously spawned function and read at most bulen bytes
// into buf. The function returns -1 on error or the number of
// actually read bytes.
int complete_secure(secure_func_t f, char *buf, size_t buflen) {
- // FIXME: read from the child pipe
- // FIXME: waitpid() for the child to exit
+ // Read from the child pipe
+ int len = read(f.pipe, buf, buflen);
+
+ // Wait for the process to exit (or to get killed by the seccomp filter)
+ int wstatus;
+ if (waitpid(f.pid, &wstatus, 0) < 0)
+ return -1;
+
+ // Only if the process exited normally and with exit state 0, the
+ // function might have completed correctly
+ if (WIFEXITED(wstatus) && (WEXITSTATUS(wstatus) == 0)) {
+ return len;
+ }
return -1;
}