Security:Seccomp

From Tizen Wiki
(Redirected from Security/Seccomp)
Jump to: navigation, search

What is seccomp

Short description

seccomp stands for secure computing mode. This is a kernel side implementation of process sandboxing. Process can swith to secure mode using prctl() system call with option PR_SET_SECCOMP set to 1. In all versions of seccomp this is a one-way transition, which is achieved by calling prctl() with PR_SET_NO_NEW_PRIVS (moreover, this is required for seccomp to work at all). This changes no_new_privs attribute of current thread inside kernel. There is no provided way to unset this attribute.

The early version of seccomp allowed only a specific set of system calls to be used by process in secure mode: exit(), sigreturn() and write(), read() to already opened file descriptors. Using any other system call resulted in kernel terminating process with SIGKILL signal.

seccomp-bpf

seccomp-bpf is an extension created for more flexible usage of seccomp based on Berkely Packet Filter. It allows more advanced way of controlling access to system. With it we can not only deny of calling specific system calls (like first version of seccomp was only capable of), but we can specify rules for any of them and even require additional arguments comparison. Setting of seccomp-bpf consists of two parts: default behaviour for calling system calls, which does not have any rules specified and set of rules for specific system calls with set behaviour (different from default one) and optional argument(s) comparison.

Actions, which can be performed as default behaviour or specified for concrete seccomp rule can be:

  • allow
  • deny with error return value (with given errno)
  • deny with signal trap
  • deny with process termination

Arguments comparison includes: EQ (equal), LE (less or equal), LT (less than), GE (greater or equal), GT (greater than), NE (not equal). In principle, one is able to perform any comparison of numeric arguments.

All of this is performed by strictly using BPF filters with instructions given in a assembler like manner and architecture dependent. Quickly this started to seem too complicated and prone to errors and thus, when kernel patches for seccomp has reached proper level of stabilization, libseccomp library was announced.

Example usage

Code needed only for killing process, which uses ptrace() system call, using pure seccomp with default setting to allow bpf looks like this:

   struct sock_filter filter[] = {
       BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr),
       BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ptrace, 1, 0),
       BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
       BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
   };
   struct sock_fprog prog = {(unsigned short) (sizeof(filter) / sizeof(filter[0])), filter };
   prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
   prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);

Libseccomp

Libseccomp library was created to enable easier and architecture independent usage of seccomp filters. It's API includes:

  • many usefull defines or enums, that facilitate the overall usage of seccomp
  • functions for:
    • initializing default seccomp action
    • adding simple rules
    • adding rules with arguments comparisons
    • loading all of the above into kernel

Example Usage

To provide the same behaviour as in seccomp-bpf example, with libseccomp we can just write:

   scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
   seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace),0);
   seccomp_load(ctx);
   seccomp_release(ctx);

Performance Analysis

To check how does enabling and loading seccomp filters for process impact it's performance, we performed few tests with different seccomp settings and for two different architectures. The time was measured using getrusage() system call to determine only the length of time spent by process in kernel.

Analyser was calling the "profiling" function , which included few system calls and one of them was repeated 100 000 (on arm) or 1000 000 (on intel) times. Every run was repeated (in below examples this was set to 10 repeats) and only the average time was taken. The same function was used for runs when seccomp was disabled and when it was enabled.

(Almost) All of the system calls were split into three groups : system calls used by function, which had no sane possibility of arguments comparisons (like nanosleep , which as arguments gets two pointers); system calls used by function, for which arguments can be easily compared (for e.g.: "read() only from fd < 20") and unused by function system calls, for which we created rules only to load seccomp more. No more than 130 rules were created, as there probably won't be need for such amount of them.

Every test case was made of these steps:

  1. Run and measure the time of function call without seccomp
  2. Set default seccomp action
  3. Create required seccomp rules (from 10 to 130)
  4. Fork, load seccomp to kernel, run and measure the time of function call
  5. Repeat until there are amount of rules is less-equal than 130.
    Increment required amount of rules by 2.

Used seccomp settings:

  1. default action - ALLOW, unused syscalls set to - deny with ERRNO
  2. default action - ERRNO, used syscalls set to - ALLOW (no argument comparisons for this setting),
    unused syscalls set to - deny with TRAP
  3. default action - ALLOW, used syscalls with inverted comparisons set to - deny with ERRNO
    (e.g. if we can read() from fds less than 20, then we deny any read() from fds >= 20), unused syscalls set to - deny with ERRNO
  4. default action - ERRNO, used syscalls (both with and without comparisons) set to - ALLOW,
    unused syscalls set to - deny with TRAP

Order of creating seccomp rules (using libseccomp API):

  1. rules for used syscalls without argument comparisons
  2. rules for used syscalls with argument comparisons
  3. rules for unused syscalls

All these 4 test cases were run on two different machines - PC computer with Ubuntu 12.04 and Intel i7, mobile device running with Tizen on ARM. Both these architectures vary very much on speed of performing test cases and in time overhead when seccomp was enabled.

Results

Intel i7 ARM Cortex-A9
Seccomp inteli7 allow simple.jpg
Seccomp arm simple allow.jpg
Seccomp inteli7 errno simple.jpg
Seccomp arm simple errno.jpg
Seccomp i7 extended allow.jpg
Seccomp arm extended allow.jpg
Seccomp i7 extended errno.jpg
Seccomp arm extended errno.jpg


Summary

After analysing results of performance analysis, we decided to perform one more test, when there's only one rule specified - for ptrace() system call. The result were highly in favor of Intel i7 (10 times better).

Intel i7 seccomp one rule kernel performance
seccomp disabled seccomp enabled Overhead
0.664041 s 0.679464 s 2%
ARM Cortex-A9 seccomp one rule kernel performance
seccomp disabled seccomp enabled Overhead
0.664222 s 0.799778 s 20%

External Links

http://lwn.net/Articles/491308/

http://lwn.net/Articles/332974/

http://sourceforge.net/projects/libseccomp/