4

I have just started learning about seccomp filters and I am using libseccomp v2.4.4. I tried to write a basic whitelisting filter that will only allow writing to the file named file1 but I am getting a "Bad system call" message in STDOUT. Here is my code:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <seccomp.h>
#include <stdlib.h>
#include <errno.h>

int main(void)
{
        FILE *fil = fopen("file1", "w");
        scmp_filter_ctx filter = seccomp_init(SCMP_ACT_KILL);

        if (filter==NULL || NULL==fil)
                goto End1;

        int chk1 = seccomp_rule_add(filter, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
                                    SCMP_A0(SCMP_CMP_EQ, fileno(fil)));
        int chk2 = seccomp_load(filter);
        if (chk1<0 || chk2<0)
                goto End;

        fprintf(stdout,"Filter did not work correctly\n");
        fprintf(fil,"Filter worked correctly\n");

End:
        seccomp_release(filter); //releasing filter before closing file
        fclose(fil);

End1:
        return 0;
}

Additionally, I see no output in file1. I am just a beginner and unsure about a lot of things here as I have written the code from my understanding, not from some reference. What seems to be the issue?


Edit: I removed the filter completely and simply executed

int main(void)
{
        FILE *fil = fopen("file1", "w");

        fprintf(stdout,"Filter did not work correctly\n");
        fprintf(fil,"Filter worked correctly\n");

End:
        fclose(fil);

End1:
        return 0;
}

Stracing this I observed that syscalls fstat,close as mentioned in the answer below by @pchaigno and additionally mmap,munmap were being called. So I must allow these syscalls for the expected behaviour.

1 Answers1

6

Debugging

Stracing your application will reveal the issue:

$ strace ./test
[...]
seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=12, filter=0x55ace60a6600}) = 0
fstat(1,  <unfinished ...>)             = ?
+++ killed by SIGSYS (core dumped) +++
Bad system call (core dumped)

Allow minimum set of syscalls

You need to allow a lot more syscalls than just write(2) for the end of your program to proceed without errors.

You'll need fstat(2) for fprintf(), close(2) for fclose(), and exit_group(2) to end the process.

Deny syscall instead of killing process

In addition, if you want your program to continue running after an unauthorized syscall, you shouldn't kill it with SCMP_ACT_KILL. SCMP_ACT_ERRNO(EPERM) is probably more appropriate in this context.

Result

int main(void) {
        FILE *fil = fopen("file1", "w");
        scmp_filter_ctx filter = seccomp_init(SCMP_ACT_ERRNO(EPERM));
        if (filter == NULL || NULL == fil)
                return 1;
        if (seccomp_rule_add(filter, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ,fileno(fil))) < 0)
                goto err;
        if (seccomp_rule_add(filter, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0) < 0)
                goto err;
        if (seccomp_rule_add(filter, SCMP_ACT_ALLOW, SCMP_SYS(close), 0) < 0)
                goto err;
        if (seccomp_rule_add(filter, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0) < 0)
                goto err;
        if (seccomp_load(filter) < 0)
                goto err;
        fprintf(stdout, "Filter did not work correctly\n");
        fprintf(fil, "Filter worked correctly\n");
err:
        seccomp_release(filter); //releasing filter before closing file
        fclose(fil);
        return 0;
}

gives us:

$ ./test; echo file1
Filter worked correctly
pchaigno
  • 11,313
  • 2
  • 29
  • 54
  • Thanks for the brilliant answer. I have some follow up questions: 1. Do we need to allow `fstat` syscall, does allowing `write` syscall not enable `fprintf`? The programme is executing correctly without allowing `fstat`. 2. Do we need to allow `close` syscall? I am using `fclose` statement after releasing the filter. I tried to write to `fil` after the close statement without allowing `close` syscall, and it came up with a segmentation fault, indicating that the file *was* closed. Looks like the program is running correctly with just the addition of `exit_group`! – Shubham Johri Jun 10 '21 at 11:57
  • I'm not sure about `fstat(2)`, but please note that, even though the program exits without error, it doesn't mean it's behaving as expected. For example, in the case of `fclose`, the fact that you can't write to `fil` anymore doesn't mean the file descriptor is considered closed by the kernel. You may still be leaking file descriptors. Also, did you check that `fprintf` returns a zero error code when `fstat(2)` is denied by seccomp? – pchaigno Jun 10 '21 at 13:36
  • Hi, thanks for your response. I removed the seccomp filter and straced the program which just contained the actions of opening the file, writing to it and stdout and then closing the file. Turns out, there were indeed calls to `fstat`,`close` and additionally `mmap`. So I must allow these syscalls for getting expected behaviour from my filter. Thank you for introducing me to `strace`! – Shubham Johri Jun 10 '21 at 19:43