Pages

Monday, February 7, 2011

Quick prctl(PR_SET_SECCOMP) Example

Using prctl(PR_SET_SECCOMP) allows a process -- running without any special permissions -- to restrict itself to only 4 system calls: read(2), write(2), _exit(2), and sigreturn(2). Anything else results in the process getting slapped with a SIGKILL. Useful, maybe, for sandboxing.

Trying it out by looking at the man page:
prctl(PR_SET_SECCOMP, 0x1, 0, 0, 0) = 0
+++ killed by SIGKILL +++
Killed

prctl returns 0 (success) but then is killed when it calls _exit(2).

Thanks to this ticket for an explanation and working code.

#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <asm/unistd.h>

    int
main(int argc, char *argv[])
{
    if (prctl(PR_SET_SECCOMP, 1, 0, 0, 0) != 0)
        (void)write(STDERR_FILENO, "prctl failed\n", 13);
    else
        (void)write(STDOUT_FILENO, "prctl ok\n", 9);

    (void)syscall(__NR_exit);
}

And here is an example that echoes back any data typed in on standard input with the length prepended:

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <asm/unistd.h>

void length();


    int
main(int argc, char *argv[])
{
    if (prctl(PR_SET_SECCOMP, 1, 0, 0, 0) != 0) {
        (void)write(STDERR_FILENO, "prctl failed\n", 13);
    }
    else {
        (void)write(STDOUT_FILENO, "prctl ok\n", 9);
        length();
    }

    (void)syscall(__NR_exit);
}

    void
length()
{
    char buf[1024];
    char num[256];
    size_t n = 0;

    for ( ; ; ) {
        (void)memset(buf, 0, sizeof(buf));
        (void)memset(num, 0, sizeof(num));

        n = read(STDIN_FILENO, buf, sizeof(buf)-1);

        if (n <= 0)
            return;

        (void)snprintf(num, sizeof(num), "%u:", n);

        (void)write(STDOUT_FILENO, num, strlen(num));
        (void)write(STDOUT_FILENO, buf, strlen(buf));
    }
}