Tuesday, February 22, 2022

[SOLVED] Inconsistent seccomp behavior

Issue

I am working on containing different applications using Linux's seccomp and I got to an inconsistency I cannot explain.

I have tried to give you examples clear enough to reproduce the problem.

I am creating a "protector module" which disallows process calling set_robust_list (for the sake of demoing the problem). Then I run processes where I inject this "protector module" using LD_PRELOAD and expect the process to stop when this system call is made.

I am making a shared object based on this code:

#include <seccomp.h>
#include <sys/prctl.h>

static void  __attribute__((constructor))  Initialization(void) {
   scmp_filter_ctx ctx;
   prctl(PR_SET_NO_NEW_PRIVS, 1);
   ctx = seccomp_init(SCMP_ACT_ALLOW); 
   seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(set_robust_list), 0);
   seccomp_load(ctx);
}

I am building it with gcc -shared seccompdemo.c -lseccomp -o libseccompdemo.so.

Then to test it I'm building this executable:

#define _GNU_SOURCE       
#include <unistd.h>
#include <sys/syscall.h>  

int main() {
   syscall(SYS_set_robust_list,0,0);
   return 0;
}

I'm building this with gcc set_robust_list.c -o set_robust_list.

Then as expected I'm runninig this executable with the above so and it gets killed with a signal:

$ LD_PRELOAD=./libseccompdemo.so ./set_robust_list
Bad system call (core dumped)

The problem is when I'm trying to do the same trick with Java.

I'm invoking the same "protector module" on java it does not seem to work despite I know Java is calling set_robust_list from strace:

$ LD_PRELOAD=./libseccompdemo.so java FileWriterTest /tmp/hosts < /etc/hosts
$ echo $?
0

See the strace output proves that java is calling 'set_robust_list':

$ strace -f java FileWriterTest /tmp/hosts < /etc/hosts 2>&1 | grep set_robust_list
set_robust_list(0x7f0b168af660, 24)     = 0
[pid 12847] set_robust_list(0x7f0b168ad9e0, 24 <unfinished ...>
[pid 12847] <... set_robust_list resumed> ) = 0
[pid 12848] set_robust_list(0x7f0b12b259e0, 24) = 0

I do see that java calls clone system call essentially for creating threads. I thought maybe seccomp filters are not inherited, but according to the documentation they are.

I'd be very glad if someone could explain me why this is not working.

For reference here is the Java code:

import java.io.FileOutputStream;
import java.io.IOException;

public class FileWriterTest {

    public static void main(String[] args) {            
        try {
            FileOutputStream f = new FileOutputStream(args[0]);
            f.write(System.in.readAllBytes());
        }
        catch (IOException e) {
            System.out.format("Caught exception: "+e.toString());
        }
    }
}

Solution

That Bad system call (core dumped) message is your shell telling you that the child process exited due to a SIGSYS signal. But if SIGSYS is blocked, the system call would just return an error, which would be handled in an application-specific manner.

I guess that pthread_create blocks signals while executing and therefore set_robust_list is only called with SIGSYS blocked, unlike your example code which doesn't modify the signal mask.

Anyways, it shouldn't really affect what you're trying to accomplish: Add a System.out.println("Hello from Java!"); to your java main and you will see it won't print if you preload the segcomp filters, because main is never called as expected.



Answered By - a3f
Answer Checked By - Willingham (WPSolving Volunteer)