| Index: sandbox/linux/seccomp-bpf/syscall.cc
|
| diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
|
| index fd599e893b2b7d3b3fdefe81dfcfeb8dd17717d4..64c0b8eb9b41b708482571febc4d417b3e1110c4 100644
|
| --- a/sandbox/linux/seccomp-bpf/syscall.cc
|
| +++ b/sandbox/linux/seccomp-bpf/syscall.cc
|
| @@ -11,169 +11,177 @@
|
|
|
| namespace sandbox {
|
|
|
| - asm( // We need to be able to tell the kernel exactly where we made a
|
| - // system call. The C++ compiler likes to sometimes clone or
|
| - // inline code, which would inadvertently end up duplicating
|
| - // the entry point.
|
| - // "gcc" can suppress code duplication with suitable function
|
| - // attributes, but "clang" doesn't have this ability.
|
| - // The "clang" developer mailing list suggested that the correct
|
| - // and portable solution is a file-scope assembly block.
|
| - // N.B. We do mark our code as a proper function so that backtraces
|
| - // work correctly. But we make absolutely no attempt to use the
|
| - // ABI's calling conventions for passing arguments. We will only
|
| - // ever be called from assembly code and thus can pick more
|
| - // suitable calling conventions.
|
| +namespace {
|
| +
|
| +asm(// We need to be able to tell the kernel exactly where we made a
|
| + // system call. The C++ compiler likes to sometimes clone or
|
| + // inline code, which would inadvertently end up duplicating
|
| + // the entry point.
|
| + // "gcc" can suppress code duplication with suitable function
|
| + // attributes, but "clang" doesn't have this ability.
|
| + // The "clang" developer mailing list suggested that the correct
|
| + // and portable solution is a file-scope assembly block.
|
| + // N.B. We do mark our code as a proper function so that backtraces
|
| + // work correctly. But we make absolutely no attempt to use the
|
| + // ABI's calling conventions for passing arguments. We will only
|
| + // ever be called from assembly code and thus can pick more
|
| + // suitable calling conventions.
|
| #if defined(__i386__)
|
| - ".text\n"
|
| - ".align 16, 0x90\n"
|
| - ".type SyscallAsm, @function\n"
|
| - "SyscallAsm:.cfi_startproc\n"
|
| - // Check if "%eax" is negative. If so, do not attempt to make a
|
| - // system call. Instead, compute the return address that is visible
|
| - // to the kernel after we execute "int $0x80". This address can be
|
| - // used as a marker that BPF code inspects.
|
| - "test %eax, %eax\n"
|
| - "jge 1f\n"
|
| - // Always, make sure that our code is position-independent, or
|
| - // address space randomization might not work on i386. This means,
|
| - // we can't use "lea", but instead have to rely on "call/pop".
|
| - "call 0f; .cfi_adjust_cfa_offset 4\n"
|
| - "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
|
| - "addl $2f-0b, %eax\n"
|
| - "ret\n"
|
| - // Save register that we don't want to clobber. On i386, we need to
|
| - // save relatively aggressively, as there are a couple or registers
|
| - // that are used internally (e.g. %ebx for position-independent
|
| - // code, and %ebp for the frame pointer), and as we need to keep at
|
| - // least a few registers available for the register allocator.
|
| - "1:push %esi; .cfi_adjust_cfa_offset 4\n"
|
| - "push %edi; .cfi_adjust_cfa_offset 4\n"
|
| - "push %ebx; .cfi_adjust_cfa_offset 4\n"
|
| - "push %ebp; .cfi_adjust_cfa_offset 4\n"
|
| - // Copy entries from the array holding the arguments into the
|
| - // correct CPU registers.
|
| - "movl 0(%edi), %ebx\n"
|
| - "movl 4(%edi), %ecx\n"
|
| - "movl 8(%edi), %edx\n"
|
| - "movl 12(%edi), %esi\n"
|
| - "movl 20(%edi), %ebp\n"
|
| - "movl 16(%edi), %edi\n"
|
| - // Enter the kernel.
|
| - "int $0x80\n"
|
| - // This is our "magic" return address that the BPF filter sees.
|
| - "2:"
|
| - // Restore any clobbered registers that we didn't declare to the
|
| - // compiler.
|
| - "pop %ebp; .cfi_adjust_cfa_offset -4\n"
|
| - "pop %ebx; .cfi_adjust_cfa_offset -4\n"
|
| - "pop %edi; .cfi_adjust_cfa_offset -4\n"
|
| - "pop %esi; .cfi_adjust_cfa_offset -4\n"
|
| - "ret\n"
|
| - ".cfi_endproc\n"
|
| - "9:.size SyscallAsm, 9b-SyscallAsm\n"
|
| + ".text\n"
|
| + ".align 16, 0x90\n"
|
| + ".type SyscallAsm, @function\n"
|
| + "SyscallAsm:.cfi_startproc\n"
|
| + // Check if "%eax" is negative. If so, do not attempt to make a
|
| + // system call. Instead, compute the return address that is visible
|
| + // to the kernel after we execute "int $0x80". This address can be
|
| + // used as a marker that BPF code inspects.
|
| + "test %eax, %eax\n"
|
| + "jge 1f\n"
|
| + // Always, make sure that our code is position-independent, or
|
| + // address space randomization might not work on i386. This means,
|
| + // we can't use "lea", but instead have to rely on "call/pop".
|
| + "call 0f; .cfi_adjust_cfa_offset 4\n"
|
| + "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
|
| + "addl $2f-0b, %eax\n"
|
| + "ret\n"
|
| + // Save register that we don't want to clobber. On i386, we need to
|
| + // save relatively aggressively, as there are a couple or registers
|
| + // that are used internally (e.g. %ebx for position-independent
|
| + // code, and %ebp for the frame pointer), and as we need to keep at
|
| + // least a few registers available for the register allocator.
|
| + "1:push %esi; .cfi_adjust_cfa_offset 4\n"
|
| + "push %edi; .cfi_adjust_cfa_offset 4\n"
|
| + "push %ebx; .cfi_adjust_cfa_offset 4\n"
|
| + "push %ebp; .cfi_adjust_cfa_offset 4\n"
|
| + // Copy entries from the array holding the arguments into the
|
| + // correct CPU registers.
|
| + "movl 0(%edi), %ebx\n"
|
| + "movl 4(%edi), %ecx\n"
|
| + "movl 8(%edi), %edx\n"
|
| + "movl 12(%edi), %esi\n"
|
| + "movl 20(%edi), %ebp\n"
|
| + "movl 16(%edi), %edi\n"
|
| + // Enter the kernel.
|
| + "int $0x80\n"
|
| + // This is our "magic" return address that the BPF filter sees.
|
| + "2:"
|
| + // Restore any clobbered registers that we didn't declare to the
|
| + // compiler.
|
| + "pop %ebp; .cfi_adjust_cfa_offset -4\n"
|
| + "pop %ebx; .cfi_adjust_cfa_offset -4\n"
|
| + "pop %edi; .cfi_adjust_cfa_offset -4\n"
|
| + "pop %esi; .cfi_adjust_cfa_offset -4\n"
|
| + "ret\n"
|
| + ".cfi_endproc\n"
|
| + "9:.size SyscallAsm, 9b-SyscallAsm\n"
|
| #elif defined(__x86_64__)
|
| - ".text\n"
|
| - ".align 16, 0x90\n"
|
| - ".type SyscallAsm, @function\n"
|
| - "SyscallAsm:.cfi_startproc\n"
|
| - // Check if "%rax" is negative. If so, do not attempt to make a
|
| - // system call. Instead, compute the return address that is visible
|
| - // to the kernel after we execute "syscall". This address can be
|
| - // used as a marker that BPF code inspects.
|
| - "test %rax, %rax\n"
|
| - "jge 1f\n"
|
| - // Always make sure that our code is position-independent, or the
|
| - // linker will throw a hissy fit on x86-64.
|
| - "call 0f; .cfi_adjust_cfa_offset 8\n"
|
| - "0:pop %rax; .cfi_adjust_cfa_offset -8\n"
|
| - "addq $2f-0b, %rax\n"
|
| - "ret\n"
|
| - // We declared all clobbered registers to the compiler. On x86-64,
|
| - // there really isn't much of a problem with register pressure. So,
|
| - // we can go ahead and directly copy the entries from the arguments
|
| - // array into the appropriate CPU registers.
|
| - "1:movq 0(%r12), %rdi\n"
|
| - "movq 8(%r12), %rsi\n"
|
| - "movq 16(%r12), %rdx\n"
|
| - "movq 24(%r12), %r10\n"
|
| - "movq 32(%r12), %r8\n"
|
| - "movq 40(%r12), %r9\n"
|
| - // Enter the kernel.
|
| - "syscall\n"
|
| - // This is our "magic" return address that the BPF filter sees.
|
| - "2:ret\n"
|
| - ".cfi_endproc\n"
|
| - "9:.size SyscallAsm, 9b-SyscallAsm\n"
|
| + ".text\n"
|
| + ".align 16, 0x90\n"
|
| + ".type SyscallAsm, @function\n"
|
| + "SyscallAsm:.cfi_startproc\n"
|
| + // Check if "%rax" is negative. If so, do not attempt to make a
|
| + // system call. Instead, compute the return address that is visible
|
| + // to the kernel after we execute "syscall". This address can be
|
| + // used as a marker that BPF code inspects.
|
| + "test %rax, %rax\n"
|
| + "jge 1f\n"
|
| + // Always make sure that our code is position-independent, or the
|
| + // linker will throw a hissy fit on x86-64.
|
| + "call 0f; .cfi_adjust_cfa_offset 8\n"
|
| + "0:pop %rax; .cfi_adjust_cfa_offset -8\n"
|
| + "addq $2f-0b, %rax\n"
|
| + "ret\n"
|
| + // We declared all clobbered registers to the compiler. On x86-64,
|
| + // there really isn't much of a problem with register pressure. So,
|
| + // we can go ahead and directly copy the entries from the arguments
|
| + // array into the appropriate CPU registers.
|
| + "1:movq 0(%r12), %rdi\n"
|
| + "movq 8(%r12), %rsi\n"
|
| + "movq 16(%r12), %rdx\n"
|
| + "movq 24(%r12), %r10\n"
|
| + "movq 32(%r12), %r8\n"
|
| + "movq 40(%r12), %r9\n"
|
| + // Enter the kernel.
|
| + "syscall\n"
|
| + // This is our "magic" return address that the BPF filter sees.
|
| + "2:ret\n"
|
| + ".cfi_endproc\n"
|
| + "9:.size SyscallAsm, 9b-SyscallAsm\n"
|
| #elif defined(__arm__)
|
| - // Throughout this file, we use the same mode (ARM vs. thumb)
|
| - // that the C++ compiler uses. This means, when transfering control
|
| - // from C++ to assembly code, we do not need to switch modes (e.g.
|
| - // by using the "bx" instruction). It also means that our assembly
|
| - // code should not be invoked directly from code that lives in
|
| - // other compilation units, as we don't bother implementing thumb
|
| - // interworking. That's OK, as we don't make any of the assembly
|
| - // symbols public. They are all local to this file.
|
| - ".text\n"
|
| - ".align 2\n"
|
| - ".type SyscallAsm, %function\n"
|
| + // Throughout this file, we use the same mode (ARM vs. thumb)
|
| + // that the C++ compiler uses. This means, when transfering control
|
| + // from C++ to assembly code, we do not need to switch modes (e.g.
|
| + // by using the "bx" instruction). It also means that our assembly
|
| + // code should not be invoked directly from code that lives in
|
| + // other compilation units, as we don't bother implementing thumb
|
| + // interworking. That's OK, as we don't make any of the assembly
|
| + // symbols public. They are all local to this file.
|
| + ".text\n"
|
| + ".align 2\n"
|
| + ".type SyscallAsm, %function\n"
|
| #if defined(__thumb__)
|
| - ".thumb_func\n"
|
| + ".thumb_func\n"
|
| #else
|
| - ".arm\n"
|
| + ".arm\n"
|
| #endif
|
| - "SyscallAsm:.fnstart\n"
|
| - "@ args = 0, pretend = 0, frame = 8\n"
|
| - "@ frame_needed = 1, uses_anonymous_args = 0\n"
|
| + "SyscallAsm:.fnstart\n"
|
| + "@ args = 0, pretend = 0, frame = 8\n"
|
| + "@ frame_needed = 1, uses_anonymous_args = 0\n"
|
| #if defined(__thumb__)
|
| - ".cfi_startproc\n"
|
| - "push {r7, lr}\n"
|
| - ".cfi_offset 14, -4\n"
|
| - ".cfi_offset 7, -8\n"
|
| - "mov r7, sp\n"
|
| - ".cfi_def_cfa_register 7\n"
|
| - ".cfi_def_cfa_offset 8\n"
|
| + ".cfi_startproc\n"
|
| + "push {r7, lr}\n"
|
| + ".cfi_offset 14, -4\n"
|
| + ".cfi_offset 7, -8\n"
|
| + "mov r7, sp\n"
|
| + ".cfi_def_cfa_register 7\n"
|
| + ".cfi_def_cfa_offset 8\n"
|
| #else
|
| - "stmfd sp!, {fp, lr}\n"
|
| - "add fp, sp, #4\n"
|
| + "stmfd sp!, {fp, lr}\n"
|
| + "add fp, sp, #4\n"
|
| #endif
|
| - // Check if "r0" is negative. If so, do not attempt to make a
|
| - // system call. Instead, compute the return address that is visible
|
| - // to the kernel after we execute "swi 0". This address can be
|
| - // used as a marker that BPF code inspects.
|
| - "cmp r0, #0\n"
|
| - "bge 1f\n"
|
| - "adr r0, 2f\n"
|
| - "b 2f\n"
|
| - // We declared (almost) all clobbered registers to the compiler. On
|
| - // ARM there is no particular register pressure. So, we can go
|
| - // ahead and directly copy the entries from the arguments array
|
| - // into the appropriate CPU registers.
|
| - "1:ldr r5, [r6, #20]\n"
|
| - "ldr r4, [r6, #16]\n"
|
| - "ldr r3, [r6, #12]\n"
|
| - "ldr r2, [r6, #8]\n"
|
| - "ldr r1, [r6, #4]\n"
|
| - "mov r7, r0\n"
|
| - "ldr r0, [r6, #0]\n"
|
| - // Enter the kernel
|
| - "swi 0\n"
|
| - // Restore the frame pointer. Also restore the program counter from
|
| - // the link register; this makes us return to the caller.
|
| + // Check if "r0" is negative. If so, do not attempt to make a
|
| + // system call. Instead, compute the return address that is visible
|
| + // to the kernel after we execute "swi 0". This address can be
|
| + // used as a marker that BPF code inspects.
|
| + "cmp r0, #0\n"
|
| + "bge 1f\n"
|
| + "adr r0, 2f\n"
|
| + "b 2f\n"
|
| + // We declared (almost) all clobbered registers to the compiler. On
|
| + // ARM there is no particular register pressure. So, we can go
|
| + // ahead and directly copy the entries from the arguments array
|
| + // into the appropriate CPU registers.
|
| + "1:ldr r5, [r6, #20]\n"
|
| + "ldr r4, [r6, #16]\n"
|
| + "ldr r3, [r6, #12]\n"
|
| + "ldr r2, [r6, #8]\n"
|
| + "ldr r1, [r6, #4]\n"
|
| + "mov r7, r0\n"
|
| + "ldr r0, [r6, #0]\n"
|
| + // Enter the kernel
|
| + "swi 0\n"
|
| +// Restore the frame pointer. Also restore the program counter from
|
| +// the link register; this makes us return to the caller.
|
| #if defined(__thumb__)
|
| - "2:pop {r7, pc}\n"
|
| - ".cfi_endproc\n"
|
| + "2:pop {r7, pc}\n"
|
| + ".cfi_endproc\n"
|
| #else
|
| - "2:ldmfd sp!, {fp, pc}\n"
|
| + "2:ldmfd sp!, {fp, pc}\n"
|
| #endif
|
| - ".fnend\n"
|
| - "9:.size SyscallAsm, 9b-SyscallAsm\n"
|
| + ".fnend\n"
|
| + "9:.size SyscallAsm, 9b-SyscallAsm\n"
|
| #endif
|
| - ); // asm
|
| + ); // asm
|
| +
|
| +} // namespace
|
|
|
| -intptr_t SandboxSyscall(int nr,
|
| - intptr_t p0, intptr_t p1, intptr_t p2,
|
| - intptr_t p3, intptr_t p4, intptr_t p5) {
|
| +intptr_t Syscall::Call(int nr,
|
| + intptr_t p0,
|
| + intptr_t p1,
|
| + intptr_t p2,
|
| + intptr_t p3,
|
| + intptr_t p4,
|
| + intptr_t p5) {
|
| // We rely on "intptr_t" to be the exact size as a "void *". This is
|
| // typically true, but just in case, we add a check. The language
|
| // specification allows platforms some leeway in cases, where
|
| @@ -181,61 +189,78 @@ intptr_t SandboxSyscall(int nr,
|
| // that this would only be an issue for IA64, which we are currently not
|
| // planning on supporting. And it is even possible that this would work
|
| // on IA64, but for lack of actual hardware, I cannot test.
|
| - COMPILE_ASSERT(sizeof(void *) == sizeof(intptr_t),
|
| + COMPILE_ASSERT(sizeof(void*) == sizeof(intptr_t),
|
| pointer_types_and_intptr_must_be_exactly_the_same_size);
|
|
|
| - const intptr_t args[6] = { p0, p1, p2, p3, p4, p5 };
|
| + const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
|
|
|
| - // Invoke our file-scope assembly code. The constraints have been picked
|
| - // carefully to match what the rest of the assembly code expects in input,
|
| - // output, and clobbered registers.
|
| +// Invoke our file-scope assembly code. The constraints have been picked
|
| +// carefully to match what the rest of the assembly code expects in input,
|
| +// output, and clobbered registers.
|
| #if defined(__i386__)
|
| intptr_t ret = nr;
|
| asm volatile(
|
| - "call SyscallAsm\n"
|
| - // N.B. These are not the calling conventions normally used by the ABI.
|
| - : "=a"(ret)
|
| - : "0"(ret), "D"(args)
|
| - : "cc", "esp", "memory", "ecx", "edx");
|
| + "call SyscallAsm\n"
|
| + // N.B. These are not the calling conventions normally used by the ABI.
|
| + : "=a"(ret)
|
| + : "0"(ret), "D"(args)
|
| + : "cc", "esp", "memory", "ecx", "edx");
|
| #elif defined(__x86_64__)
|
| intptr_t ret = nr;
|
| {
|
| - register const intptr_t *data __asm__("r12") = args;
|
| + register const intptr_t* data __asm__("r12") = args;
|
| asm volatile(
|
| - "lea -128(%%rsp), %%rsp\n" // Avoid red zone.
|
| - "call SyscallAsm\n"
|
| - "lea 128(%%rsp), %%rsp\n"
|
| - // N.B. These are not the calling conventions normally used by the ABI.
|
| - : "=a"(ret)
|
| - : "0"(ret), "r"(data)
|
| - : "cc", "rsp", "memory",
|
| - "rcx", "rdi", "rsi", "rdx", "r8", "r9", "r10", "r11");
|
| + "lea -128(%%rsp), %%rsp\n" // Avoid red zone.
|
| + "call SyscallAsm\n"
|
| + "lea 128(%%rsp), %%rsp\n"
|
| + // N.B. These are not the calling conventions normally used by the ABI.
|
| + : "=a"(ret)
|
| + : "0"(ret), "r"(data)
|
| + : "cc",
|
| + "rsp",
|
| + "memory",
|
| + "rcx",
|
| + "rdi",
|
| + "rsi",
|
| + "rdx",
|
| + "r8",
|
| + "r9",
|
| + "r10",
|
| + "r11");
|
| }
|
| #elif defined(__arm__)
|
| intptr_t ret;
|
| {
|
| register intptr_t inout __asm__("r0") = nr;
|
| - register const intptr_t *data __asm__("r6") = args;
|
| + register const intptr_t* data __asm__("r6") = args;
|
| asm volatile(
|
| - "bl SyscallAsm\n"
|
| - // N.B. These are not the calling conventions normally used by the ABI.
|
| - : "=r"(inout)
|
| - : "0"(inout), "r"(data)
|
| - : "cc", "lr", "memory", "r1", "r2", "r3", "r4", "r5"
|
| + "bl SyscallAsm\n"
|
| + // N.B. These are not the calling conventions normally used by the ABI.
|
| + : "=r"(inout)
|
| + : "0"(inout), "r"(data)
|
| + : "cc",
|
| + "lr",
|
| + "memory",
|
| + "r1",
|
| + "r2",
|
| + "r3",
|
| + "r4",
|
| + "r5"
|
| #if !defined(__thumb__)
|
| - // In thumb mode, we cannot use "r7" as a general purpose register, as
|
| - // it is our frame pointer. We have to manually manage and preserve it.
|
| - // In ARM mode, we have a dedicated frame pointer register and "r7" is
|
| - // thus available as a general purpose register. We don't preserve it,
|
| - // but instead mark it as clobbered.
|
| - , "r7"
|
| + // In thumb mode, we cannot use "r7" as a general purpose register, as
|
| + // it is our frame pointer. We have to manually manage and preserve
|
| + // it.
|
| + // In ARM mode, we have a dedicated frame pointer register and "r7" is
|
| + // thus available as a general purpose register. We don't preserve it,
|
| + // but instead mark it as clobbered.
|
| + ,
|
| + "r7"
|
| #endif // !defined(__thumb__)
|
| - );
|
| + );
|
| ret = inout;
|
| }
|
| #else
|
| - errno = ENOSYS;
|
| - intptr_t ret = -1;
|
| +#error "Unimplemented architecture"
|
| #endif
|
| return ret;
|
| }
|
|
|