Home | History | Annotate | Download | only in seccomp-bpf
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "sandbox/linux/seccomp-bpf/syscall.h"
      6 
      7 #include <asm/unistd.h>
      8 #include <errno.h>
      9 
     10 #include "base/basictypes.h"
     11 
     12 namespace sandbox {
     13 
     14 namespace {
     15 
     16 asm(// We need to be able to tell the kernel exactly where we made a
     17     // system call. The C++ compiler likes to sometimes clone or
     18     // inline code, which would inadvertently end up duplicating
     19     // the entry point.
     20     // "gcc" can suppress code duplication with suitable function
     21     // attributes, but "clang" doesn't have this ability.
     22     // The "clang" developer mailing list suggested that the correct
     23     // and portable solution is a file-scope assembly block.
     24     // N.B. We do mark our code as a proper function so that backtraces
     25     // work correctly. But we make absolutely no attempt to use the
     26     // ABI's calling conventions for passing arguments. We will only
     27     // ever be called from assembly code and thus can pick more
     28     // suitable calling conventions.
     29 #if defined(__i386__)
     30     ".text\n"
     31     ".align 16, 0x90\n"
     32     ".type SyscallAsm, @function\n"
     33     "SyscallAsm:.cfi_startproc\n"
     34     // Check if "%eax" is negative. If so, do not attempt to make a
     35     // system call. Instead, compute the return address that is visible
     36     // to the kernel after we execute "int $0x80". This address can be
     37     // used as a marker that BPF code inspects.
     38     "test %eax, %eax\n"
     39     "jge  1f\n"
     40     // Always, make sure that our code is position-independent, or
     41     // address space randomization might not work on i386. This means,
     42     // we can't use "lea", but instead have to rely on "call/pop".
     43     "call 0f;   .cfi_adjust_cfa_offset  4\n"
     44     "0:pop  %eax; .cfi_adjust_cfa_offset -4\n"
     45     "addl $2f-0b, %eax\n"
     46     "ret\n"
     47     // Save register that we don't want to clobber. On i386, we need to
     48     // save relatively aggressively, as there are a couple or registers
     49     // that are used internally (e.g. %ebx for position-independent
     50     // code, and %ebp for the frame pointer), and as we need to keep at
     51     // least a few registers available for the register allocator.
     52     "1:push %esi; .cfi_adjust_cfa_offset 4\n"
     53     "push %edi; .cfi_adjust_cfa_offset 4\n"
     54     "push %ebx; .cfi_adjust_cfa_offset 4\n"
     55     "push %ebp; .cfi_adjust_cfa_offset 4\n"
     56     // Copy entries from the array holding the arguments into the
     57     // correct CPU registers.
     58     "movl  0(%edi), %ebx\n"
     59     "movl  4(%edi), %ecx\n"
     60     "movl  8(%edi), %edx\n"
     61     "movl 12(%edi), %esi\n"
     62     "movl 20(%edi), %ebp\n"
     63     "movl 16(%edi), %edi\n"
     64     // Enter the kernel.
     65     "int  $0x80\n"
     66     // This is our "magic" return address that the BPF filter sees.
     67     "2:"
     68     // Restore any clobbered registers that we didn't declare to the
     69     // compiler.
     70     "pop  %ebp; .cfi_adjust_cfa_offset -4\n"
     71     "pop  %ebx; .cfi_adjust_cfa_offset -4\n"
     72     "pop  %edi; .cfi_adjust_cfa_offset -4\n"
     73     "pop  %esi; .cfi_adjust_cfa_offset -4\n"
     74     "ret\n"
     75     ".cfi_endproc\n"
     76     "9:.size SyscallAsm, 9b-SyscallAsm\n"
     77 #elif defined(__x86_64__)
     78     ".text\n"
     79     ".align 16, 0x90\n"
     80     ".type SyscallAsm, @function\n"
     81     "SyscallAsm:.cfi_startproc\n"
     82     // Check if "%rax" is negative. If so, do not attempt to make a
     83     // system call. Instead, compute the return address that is visible
     84     // to the kernel after we execute "syscall". This address can be
     85     // used as a marker that BPF code inspects.
     86     "test %rax, %rax\n"
     87     "jge  1f\n"
     88     // Always make sure that our code is position-independent, or the
     89     // linker will throw a hissy fit on x86-64.
     90     "call 0f;   .cfi_adjust_cfa_offset  8\n"
     91     "0:pop  %rax; .cfi_adjust_cfa_offset -8\n"
     92     "addq $2f-0b, %rax\n"
     93     "ret\n"
     94     // We declared all clobbered registers to the compiler. On x86-64,
     95     // there really isn't much of a problem with register pressure. So,
     96     // we can go ahead and directly copy the entries from the arguments
     97     // array into the appropriate CPU registers.
     98     "1:movq  0(%r12), %rdi\n"
     99     "movq  8(%r12), %rsi\n"
    100     "movq 16(%r12), %rdx\n"
    101     "movq 24(%r12), %r10\n"
    102     "movq 32(%r12), %r8\n"
    103     "movq 40(%r12), %r9\n"
    104     // Enter the kernel.
    105     "syscall\n"
    106     // This is our "magic" return address that the BPF filter sees.
    107     "2:ret\n"
    108     ".cfi_endproc\n"
    109     "9:.size SyscallAsm, 9b-SyscallAsm\n"
    110 #elif defined(__arm__)
    111     // Throughout this file, we use the same mode (ARM vs. thumb)
    112     // that the C++ compiler uses. This means, when transfering control
    113     // from C++ to assembly code, we do not need to switch modes (e.g.
    114     // by using the "bx" instruction). It also means that our assembly
    115     // code should not be invoked directly from code that lives in
    116     // other compilation units, as we don't bother implementing thumb
    117     // interworking. That's OK, as we don't make any of the assembly
    118     // symbols public. They are all local to this file.
    119     ".text\n"
    120     ".align 2\n"
    121     ".type SyscallAsm, %function\n"
    122 #if defined(__thumb__)
    123     ".thumb_func\n"
    124 #else
    125     ".arm\n"
    126 #endif
    127     "SyscallAsm:.fnstart\n"
    128     "@ args = 0, pretend = 0, frame = 8\n"
    129     "@ frame_needed = 1, uses_anonymous_args = 0\n"
    130 #if defined(__thumb__)
    131     ".cfi_startproc\n"
    132     "push {r7, lr}\n"
    133     ".cfi_offset 14, -4\n"
    134     ".cfi_offset  7, -8\n"
    135     "mov r7, sp\n"
    136     ".cfi_def_cfa_register 7\n"
    137     ".cfi_def_cfa_offset 8\n"
    138 #else
    139     "stmfd sp!, {fp, lr}\n"
    140     "add fp, sp, #4\n"
    141 #endif
    142     // Check if "r0" is negative. If so, do not attempt to make a
    143     // system call. Instead, compute the return address that is visible
    144     // to the kernel after we execute "swi 0". This address can be
    145     // used as a marker that BPF code inspects.
    146     "cmp r0, #0\n"
    147     "bge 1f\n"
    148     "adr r0, 2f\n"
    149     "b   2f\n"
    150     // We declared (almost) all clobbered registers to the compiler. On
    151     // ARM there is no particular register pressure. So, we can go
    152     // ahead and directly copy the entries from the arguments array
    153     // into the appropriate CPU registers.
    154     "1:ldr r5, [r6, #20]\n"
    155     "ldr r4, [r6, #16]\n"
    156     "ldr r3, [r6, #12]\n"
    157     "ldr r2, [r6, #8]\n"
    158     "ldr r1, [r6, #4]\n"
    159     "mov r7, r0\n"
    160     "ldr r0, [r6, #0]\n"
    161     // Enter the kernel
    162     "swi 0\n"
    163 // Restore the frame pointer. Also restore the program counter from
    164 // the link register; this makes us return to the caller.
    165 #if defined(__thumb__)
    166     "2:pop {r7, pc}\n"
    167     ".cfi_endproc\n"
    168 #else
    169     "2:ldmfd sp!, {fp, pc}\n"
    170 #endif
    171     ".fnend\n"
    172     "9:.size SyscallAsm, 9b-SyscallAsm\n"
    173 #endif
    174     );  // asm
    175 
    176 }  // namespace
    177 
    178 intptr_t Syscall::Call(int nr,
    179                        intptr_t p0,
    180                        intptr_t p1,
    181                        intptr_t p2,
    182                        intptr_t p3,
    183                        intptr_t p4,
    184                        intptr_t p5) {
    185   // We rely on "intptr_t" to be the exact size as a "void *". This is
    186   // typically true, but just in case, we add a check. The language
    187   // specification allows platforms some leeway in cases, where
    188   // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect
    189   // that this would only be an issue for IA64, which we are currently not
    190   // planning on supporting. And it is even possible that this would work
    191   // on IA64, but for lack of actual hardware, I cannot test.
    192   COMPILE_ASSERT(sizeof(void*) == sizeof(intptr_t),
    193                  pointer_types_and_intptr_must_be_exactly_the_same_size);
    194 
    195   const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
    196 
    197 // Invoke our file-scope assembly code. The constraints have been picked
    198 // carefully to match what the rest of the assembly code expects in input,
    199 // output, and clobbered registers.
    200 #if defined(__i386__)
    201   intptr_t ret = nr;
    202   asm volatile(
    203       "call SyscallAsm\n"
    204       // N.B. These are not the calling conventions normally used by the ABI.
    205       : "=a"(ret)
    206       : "0"(ret), "D"(args)
    207       : "cc", "esp", "memory", "ecx", "edx");
    208 #elif defined(__x86_64__)
    209   intptr_t ret = nr;
    210   {
    211     register const intptr_t* data __asm__("r12") = args;
    212     asm volatile(
    213         "lea  -128(%%rsp), %%rsp\n"  // Avoid red zone.
    214         "call SyscallAsm\n"
    215         "lea  128(%%rsp), %%rsp\n"
    216         // N.B. These are not the calling conventions normally used by the ABI.
    217         : "=a"(ret)
    218         : "0"(ret), "r"(data)
    219         : "cc",
    220           "rsp",
    221           "memory",
    222           "rcx",
    223           "rdi",
    224           "rsi",
    225           "rdx",
    226           "r8",
    227           "r9",
    228           "r10",
    229           "r11");
    230   }
    231 #elif defined(__arm__)
    232   intptr_t ret;
    233   {
    234     register intptr_t inout __asm__("r0") = nr;
    235     register const intptr_t* data __asm__("r6") = args;
    236     asm volatile(
    237         "bl SyscallAsm\n"
    238         // N.B. These are not the calling conventions normally used by the ABI.
    239         : "=r"(inout)
    240         : "0"(inout), "r"(data)
    241         : "cc",
    242           "lr",
    243           "memory",
    244           "r1",
    245           "r2",
    246           "r3",
    247           "r4",
    248           "r5"
    249 #if !defined(__thumb__)
    250           // In thumb mode, we cannot use "r7" as a general purpose register, as
    251           // it is our frame pointer. We have to manually manage and preserve
    252           // it.
    253           // In ARM mode, we have a dedicated frame pointer register and "r7" is
    254           // thus available as a general purpose register. We don't preserve it,
    255           // but instead mark it as clobbered.
    256           ,
    257           "r7"
    258 #endif  // !defined(__thumb__)
    259         );
    260     ret = inout;
    261   }
    262 #else
    263 #error "Unimplemented architecture"
    264 #endif
    265   return ret;
    266 }
    267 
    268 }  // namespace sandbox
    269