chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

// Note: any code in this file MUST be async-signal safe.

#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"

#include <fcntl.h>
#include <linux/net.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>

#include "base/check.h"
#include "base/debug/crash_logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/safe_sprintf.h"
#include "build/build_config.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
#include "sandbox/linux/services/syscall_wrappers.h"
#include "sandbox/linux/system_headers/linux_seccomp.h"
#include "sandbox/linux/system_headers/linux_stat.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"

#if BUILDFLAG(IS_ANDROID)
#include <android/log.h>
#endif

#if defined(__mips__)
// __NR_Linux, is defined in <asm/unistd.h>.
#include <asm/unistd.h>
#endif

#define SECCOMP_MESSAGE_COMMON_CONTENT
#define SECCOMP_MESSAGE_CLONE_CONTENT
#define SECCOMP_MESSAGE_PRCTL_CONTENT
#define SECCOMP_MESSAGE_IOCTL_CONTENT
#define SECCOMP_MESSAGE_KILL_CONTENT
#define SECCOMP_MESSAGE_FUTEX_CONTENT
#define SECCOMP_MESSAGE_PTRACE_CONTENT
#define SECCOMP_MESSAGE_SOCKET_CONTENT
#define SECCOMP_MESSAGE_SOCKOPT_CONTENT

namespace {

#if BUILDFLAG(IS_ANDROID)
constexpr char kLogTag[] = "cr_seccomp";
#endif

base::debug::CrashKeyString* seccomp_crash_key =;

inline bool IsArchitectureX86_64() {}

// Write |error_message| to stderr. Similar to RawLog(), but a bit more careful
// about async-signal safety. |size| is the size to write and should typically
// not include a terminating \0.
void WriteToStdErr(const char* error_message, size_t size) {}

// Invalid syscall values are truncated to zero.
// On architectures where base value is zero (Intel and Arm),
// syscall number is the same as offset from base.
// This function returns values between 0 and 1023 on all architectures.
// On architectures where base value is different than zero (currently only
// Mips), we are truncating valid syscall values to offset from base.
uint32_t SyscallNumberToOffsetFromBase(uint32_t sysno) {}

// Records the syscall number and first four arguments in a crash key, to help
// debug the failure.
void PrintAndSetSeccompCrashKey(const struct arch_seccomp_data& args) {}

}  // namespace

namespace sandbox {

intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {}

// TODO(jln): refactor the reporting functions.

intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {}

intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
                            void* /* aux */) {}

intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
                            void* /* aux */) {}

intptr_t SIGSYSKillFailure(const struct arch_seccomp_data& args,
                           void* /* aux */) {}

intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
                            void* /* aux */) {}

intptr_t SIGSYSPtraceFailure(const struct arch_seccomp_data& args,
                             void* /* aux */) {}

intptr_t SIGSYSSocketFailure(const struct arch_seccomp_data& args, void* aux) {}

intptr_t SIGSYSSockoptFailure(const struct arch_seccomp_data& args, void* aux) {}

intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args,
                            void* aux) {}

intptr_t SIGSYSFstatatHandler(const struct arch_seccomp_data& args,
                              void* fs_denied_errno) {}

bpf_dsl::ResultExpr CrashSIGSYS() {}

bpf_dsl::ResultExpr CrashSIGSYSClone() {}

bpf_dsl::ResultExpr CrashSIGSYSPrctl() {}

bpf_dsl::ResultExpr CrashSIGSYSIoctl() {}

bpf_dsl::ResultExpr CrashSIGSYSKill() {}

bpf_dsl::ResultExpr CrashSIGSYSFutex() {}

bpf_dsl::ResultExpr CrashSIGSYSPtrace() {}

bpf_dsl::ResultExpr CrashSIGSYSSocket() {}

bpf_dsl::ResultExpr CrashSIGSYSSockopt() {}

bpf_dsl::ResultExpr RewriteSchedSIGSYS() {}

bpf_dsl::ResultExpr RewriteFstatatSIGSYS(int fs_denied_errno) {}

#if defined(__NR_socketcall)
bool CanRewriteSocketcall() {
  static bool can_rewrite_socketcall = []() {
    // Call socket(2) with invalid flags and see if it returns ENOSYS.
    base::ScopedFD socket_fd(
        syscall(__NR_socket, 0xffffff, 0xffffff, 0xffffff));
    if (!socket_fd.is_valid() && errno == ENOSYS) {
      return false;
    }
    return true;
  }();
  return can_rewrite_socketcall;
}

intptr_t SIGSYSSocketcallHandler(const struct arch_seccomp_data& args,
                                 void* aux) {
  const long kLastSocketcall = SYS_SENDMMSG;
  // This array is mostly copy and pasted from the Linux kernel (net/socket.c)
  static const struct {
    long sysno;
    size_t num_args;
    size_t num_zeroes = 0;
  } socketcall_args[kLastSocketcall + 1] = {
      {.sysno = -1, .num_args = 0},
      {.sysno = __NR_socket, .num_args = 3},
      {.sysno = __NR_bind, .num_args = 3},
      {.sysno = __NR_connect, .num_args = 3},
      {.sysno = __NR_listen, .num_args = 2},
      // SYS_ACCEPT does not always have a corresponding accept() syscall, but
      // always has an accept4() with flags == 0.
      {.sysno = __NR_accept4, .num_args = 3, .num_zeroes = 1},
      {.sysno = __NR_getsockname, .num_args = 3},
      {.sysno = __NR_getpeername, .num_args = 3},
      {.sysno = __NR_socketpair, .num_args = 4},
      // The SYS_SEND and SYS_RECV calls do not have corresponding __NR_send and
      // __NR_recv syscalls, but are equivalent to sendto() and recvfrom() with
      // the final arguments as 0.
      {.sysno = __NR_sendto, .num_args = 4, .num_zeroes = 2},
      {.sysno = __NR_recvfrom, .num_args = 4, .num_zeroes = 2},
      {.sysno = __NR_sendto, .num_args = 6},
      {.sysno = __NR_recvfrom, .num_args = 6},
      {.sysno = __NR_shutdown, .num_args = 2},
      {.sysno = __NR_setsockopt, .num_args = 5},
      {.sysno = __NR_getsockopt, .num_args = 5},
      {.sysno = __NR_sendmsg, .num_args = 3},
      {.sysno = __NR_recvmsg, .num_args = 3},
      {.sysno = __NR_accept4, .num_args = 4},
      {.sysno = __NR_recvmmsg, .num_args = 5},
      {.sysno = __NR_sendmmsg, .num_args = 4}};
  uint64_t call = args.args[0];
  if (args.nr == __NR_socketcall && 0 < call && call <= kLastSocketcall) {
    const size_t real_args_arr_len =
        socketcall_args[call].num_args + socketcall_args[call].num_zeroes;
// The length of this array is bounded by the entries in the array above,
// but the compiler isn't smart enough to figure that out.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wvla-extension"
    unsigned long real_args_arr[real_args_arr_len];
#pragma clang diagnostic pop
    memcpy(real_args_arr, reinterpret_cast<unsigned long*>(args.args[1]),
           real_args_arr_len * sizeof(unsigned long));
    memset(real_args_arr + socketcall_args[call].num_args, 0,
           socketcall_args[call].num_zeroes * sizeof(unsigned long));
    switch (real_args_arr_len) {
      case 2:
        return syscall(socketcall_args[call].sysno, real_args_arr[0],
                       real_args_arr[1]);
      case 3:
        return syscall(socketcall_args[call].sysno, real_args_arr[0],
                       real_args_arr[1], real_args_arr[2]);
      case 4:
        return syscall(socketcall_args[call].sysno, real_args_arr[0],
                       real_args_arr[1], real_args_arr[2], real_args_arr[3]);
      case 5:
        return syscall(socketcall_args[call].sysno, real_args_arr[0],
                       real_args_arr[1], real_args_arr[2], real_args_arr[3],
                       real_args_arr[4]);
      case 6:
        return syscall(socketcall_args[call].sysno, real_args_arr[0],
                       real_args_arr[1], real_args_arr[2], real_args_arr[3],
                       real_args_arr[4], real_args_arr[5]);
      default:
        break;
    }
  }

  CrashSIGSYS_Handler(args, aux);

  // Should never be reached.
  RAW_CHECK(false);
  return -ENOSYS;
}

bpf_dsl::ResultExpr RewriteSocketcallSIGSYS() {
  return bpf_dsl::Trap(SIGSYSSocketcallHandler, nullptr);
}
#endif  // defined(__NR_socketcall)

void AllocateCrashKeys() {}

const char* GetErrorMessageContentForTests() {}

const char* GetCloneErrorMessageContentForTests() {}

const char* GetPrctlErrorMessageContentForTests() {}

const char* GetIoctlErrorMessageContentForTests() {}

const char* GetKillErrorMessageContentForTests() {}

const char* GetFutexErrorMessageContentForTests() {}

const char* GetPtraceErrorMessageContentForTests() {}

const char* GetSocketErrorMessageContentForTests() {}

const char* GetSockoptErrorMessageContentForTests() {}

}  // namespace sandbox.