Home | History | Annotate | Download | only in seccomp-bpf-helpers
      1 // Copyright (c) 2013 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 // Note: any code in this file MUST be async-signal safe.
      6 
      7 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
      8 
      9 #include <unistd.h>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/logging.h"
     13 #include "base/posix/eintr_wrapper.h"
     14 #include "build/build_config.h"
     15 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
     16 
     17 namespace {
     18 
     19 inline bool IsArchitectureX86_64() {
     20 #if defined(__x86_64__)
     21   return true;
     22 #else
     23   return false;
     24 #endif
     25 }
     26 
     27 // Write |error_message| to stderr. Similar to RawLog(), but a bit more careful
     28 // about async-signal safety. |size| is the size to write and should typically
     29 // not include a terminating \0.
     30 void WriteToStdErr(const char* error_message, size_t size) {
     31   while (size > 0) {
     32     // TODO(jln): query the current policy to check if send() is available and
     33     // use it to perform a non-blocking write.
     34     const int ret = HANDLE_EINTR(write(STDERR_FILENO, error_message, size));
     35     // We can't handle any type of error here.
     36     if (ret <= 0 || static_cast<size_t>(ret) > size) break;
     37     size -= ret;
     38     error_message += ret;
     39   }
     40 }
     41 
     42 // Print a seccomp-bpf failure to handle |sysno| to stderr in an
     43 // async-signal safe way.
     44 void PrintSyscallError(uint32_t sysno) {
     45   if (sysno >= 1024)
     46     sysno = 0;
     47   // TODO(markus): replace with async-signal safe snprintf when available.
     48   const size_t kNumDigits = 4;
     49   char sysno_base10[kNumDigits];
     50   uint32_t rem = sysno;
     51   uint32_t mod = 0;
     52   for (int i = kNumDigits - 1; i >= 0; i--) {
     53     mod = rem % 10;
     54     rem /= 10;
     55     sysno_base10[i] = '0' + mod;
     56   }
     57   static const char kSeccompErrorPrefix[] =
     58       __FILE__":**CRASHING**:seccomp-bpf failure in syscall ";
     59   static const char kSeccompErrorPostfix[] = "\n";
     60   WriteToStdErr(kSeccompErrorPrefix, sizeof(kSeccompErrorPrefix) - 1);
     61   WriteToStdErr(sysno_base10, sizeof(sysno_base10));
     62   WriteToStdErr(kSeccompErrorPostfix, sizeof(kSeccompErrorPostfix) - 1);
     63 }
     64 
     65 }  // namespace.
     66 
     67 namespace sandbox {
     68 
     69 intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {
     70   uint32_t syscall = args.nr;
     71   if (syscall >= 1024)
     72     syscall = 0;
     73   PrintSyscallError(syscall);
     74 
     75   // Encode 8-bits of the 1st two arguments too, so we can discern which socket
     76   // type, which fcntl, ... etc., without being likely to hit a mapped
     77   // address.
     78   // Do not encode more bits here without thinking about increasing the
     79   // likelihood of collision with mapped pages.
     80   syscall |= ((args.args[0] & 0xffUL) << 12);
     81   syscall |= ((args.args[1] & 0xffUL) << 20);
     82   // Purposefully dereference the syscall as an address so it'll show up very
     83   // clearly and easily in crash dumps.
     84   volatile char* addr = reinterpret_cast<volatile char*>(syscall);
     85   *addr = '\0';
     86   // In case we hit a mapped address, hit the null page with just the syscall,
     87   // for paranoia.
     88   syscall &= 0xfffUL;
     89   addr = reinterpret_cast<volatile char*>(syscall);
     90   *addr = '\0';
     91   for (;;)
     92     _exit(1);
     93 }
     94 
     95 // TODO(jln): refactor the reporting functions.
     96 
     97 intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
     98   // "flags" is the first argument in the kernel's clone().
     99   // Mark as volatile to be able to find the value on the stack in a minidump.
    100 #if !defined(NDEBUG)
    101   RAW_LOG(ERROR, __FILE__":**CRASHING**:clone() failure\n");
    102 #endif
    103   volatile uint64_t clone_flags = args.args[0];
    104   volatile char* addr;
    105   if (IsArchitectureX86_64()) {
    106     addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFFFFF);
    107     *addr = '\0';
    108   }
    109   // Hit the NULL page if this fails to fault.
    110   addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFF);
    111   *addr = '\0';
    112   for (;;)
    113     _exit(1);
    114 }
    115 
    116 intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
    117                             void* /* aux */) {
    118   // Mark as volatile to be able to find the value on the stack in a minidump.
    119 #if !defined(NDEBUG)
    120   RAW_LOG(ERROR, __FILE__":**CRASHING**:prctl() failure\n");
    121 #endif
    122   volatile uint64_t option = args.args[0];
    123   volatile char* addr =
    124       reinterpret_cast<volatile char*>(option & 0xFFF);
    125   *addr = '\0';
    126   for (;;)
    127     _exit(1);
    128 }
    129 
    130 intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
    131                             void* /* aux */) {
    132   // Make "request" volatile so that we can see it on the stack in a minidump.
    133 #if !defined(NDEBUG)
    134   RAW_LOG(ERROR, __FILE__":**CRASHING**:ioctl() failure\n");
    135 #endif
    136   volatile uint64_t request = args.args[1];
    137   volatile char* addr = reinterpret_cast<volatile char*>(request & 0xFFFF);
    138   *addr = '\0';
    139   // Hit the NULL page if this fails.
    140   addr = reinterpret_cast<volatile char*>(request & 0xFFF);
    141   *addr = '\0';
    142   for (;;)
    143     _exit(1);
    144 }
    145 
    146 }  // namespace sandbox.
    147