diff options
Diffstat (limited to 'chromium/sandbox')
8 files changed, 301 insertions, 74 deletions
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc index a4124ba74ef..ba2d363a225 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc @@ -140,9 +140,6 @@ ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const { case __NR_socket: #endif - // Ptrace is allowed so the Breakpad Microdumper can fork in a renderer - // and then ptrace the parent. - case __NR_ptrace: override_and_allow = true; break; } @@ -152,6 +149,12 @@ ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const { return Allow(); } + // Ptrace is allowed so the crash reporter can fork in a renderer + // and then ptrace the parent. https://crbug.com/933418 + if (sysno == __NR_ptrace) { + return RestrictPtrace(); + } + // https://crbug.com/644759 if (sysno == __NR_rt_tgsigqueueinfo) { const Arg<pid_t> tgid(0); diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc index 5adc1a7a4d4..7514f9a4a0a 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc @@ -33,6 +33,7 @@ #define SECCOMP_MESSAGE_IOCTL_CONTENT "ioctl() failure" #define SECCOMP_MESSAGE_KILL_CONTENT "(tg)kill() failure" #define SECCOMP_MESSAGE_FUTEX_CONTENT "futex() failure" +#define SECCOMP_MESSAGE_PTRACE_CONTENT "ptrace() failure" namespace { @@ -305,6 +306,19 @@ intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args, _exit(1); } +intptr_t SIGSYSPtraceFailure(const struct arch_seccomp_data& args, + void* /* aux */) { + static const char kSeccompPtraceError[] = + __FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_PTRACE_CONTENT "\n"; + WriteToStdErr(kSeccompPtraceError, sizeof(kSeccompPtraceError) - 1); + SetSeccompCrashKey(args); + volatile int ptrace_op = args.args[0]; + volatile char* addr = reinterpret_cast<volatile char*>(ptrace_op & 0xFFF); + *addr = '\0'; + for (;;) + _exit(1); +} + intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args, void* aux) { switch (args.nr) { @@ -363,6 +377,10 @@ bpf_dsl::ResultExpr CrashSIGSYSFutex() { return bpf_dsl::Trap(SIGSYSFutexFailure, NULL); } +bpf_dsl::ResultExpr CrashSIGSYSPtrace() { + return bpf_dsl::Trap(SIGSYSPtraceFailure, NULL); +} + bpf_dsl::ResultExpr RewriteSchedSIGSYS() { return bpf_dsl::Trap(SIGSYSSchedHandler, NULL); } @@ -401,4 +419,8 @@ const char* GetFutexErrorMessageContentForTests() { return SECCOMP_MESSAGE_FUTEX_CONTENT; } +const char* GetPtraceErrorMessageContentForTests() { + return SECCOMP_MESSAGE_PTRACE_CONTENT; +} + } // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h index b32fd6fedd4..baac3b61447 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h @@ -25,8 +25,8 @@ struct arch_seccomp_data; SANDBOX_EXPORT intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux); -// The following three handlers are suitable to report failures with the -// clone(), prctl() and ioctl() system calls respectively. +// The following seven handlers are suitable to report failures for specific +// system calls with additional information. // The crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is // the clone(2) argument, extracted from |args|. @@ -48,6 +48,10 @@ SANDBOX_EXPORT intptr_t // argument. SANDBOX_EXPORT intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux); +// The crashing address will be (op & 0xFFF), where op is the second +// argument. +SANDBOX_EXPORT intptr_t +SIGSYSPtraceFailure(const struct arch_seccomp_data& args, void* aux); // If the syscall is not being called on the current tid, crashes in the same // way as CrashSIGSYS_Handler. Otherwise, returns the result of calling the // syscall with the pid argument set to 0 (which for these calls means the @@ -66,6 +70,7 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPrctl(); SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSIoctl(); SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill(); SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex(); +SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPtrace(); SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS(); // Allocates a crash key so that Seccomp information can be recorded. @@ -79,6 +84,7 @@ SANDBOX_EXPORT const char* GetPrctlErrorMessageContentForTests(); SANDBOX_EXPORT const char* GetIoctlErrorMessageContentForTests(); SANDBOX_EXPORT const char* GetKillErrorMessageContentForTests(); SANDBOX_EXPORT const char* GetFutexErrorMessageContentForTests(); +SANDBOX_EXPORT const char* GetPtraceErrorMessageContentForTests(); } // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc index 2577f02f2e9..55394a791a5 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc @@ -6,7 +6,6 @@ #include <errno.h> #include <fcntl.h> -#include <fcntl.h> #include <linux/net.h> #include <sched.h> #include <signal.h> @@ -31,10 +30,17 @@ #include "sandbox/linux/system_headers/linux_syscalls.h" #include "sandbox/linux/system_headers/linux_time.h" -// PNaCl toolchain does not provide sys/ioctl.h header. +// PNaCl toolchain does not provide sys/ioctl.h and sys/ptrace.h headers. #if !defined(OS_NACL_NONSFI) #include <sys/ioctl.h> -#endif +#include <sys/ptrace.h> +#if !defined(PTRACE_GET_THREAD_AREA) && defined(OS_LINUX) && \ + !defined(OS_CHROMEOS) +// Also include asm/ptrace-abi.h since ptrace.h in older libc (for instance +// the one in Ubuntu 16.04 LTS) is missing PTRACE_GET_THREAD_AREA. +#include <asm/ptrace-abi.h> +#endif // !PTRACE_GET_THREAD_AREA && OS_LINUX && !OS_CHROMEOS +#endif // !OS_NACL_NONSFI #if defined(OS_ANDROID) @@ -383,4 +389,25 @@ ResultExpr RestrictPrlimit(pid_t target_pid) { return If(AnyOf(pid == 0, pid == target_pid), Allow()).Else(Error(EPERM)); } +#if !defined(OS_NACL_NONSFI) +ResultExpr RestrictPtrace() { + const Arg<int> request(0); + return Switch(request).CASES(( +#if !defined(__aarch64__) + PTRACE_GETREGS, + PTRACE_GETFPREGS, + PTRACE_GET_THREAD_AREA, +#endif +#if defined(__arm__) + PTRACE_GETVFPREGS, +#endif + PTRACE_GETREGSET, + PTRACE_PEEKDATA, + PTRACE_ATTACH, + PTRACE_DETACH), + Allow()) + .Default(CrashSIGSYSPtrace()); +} +#endif // defined(OS_NACL_NONSFI) + } // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h index 71c56093d92..cb563dfc550 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h @@ -103,6 +103,10 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetRandom(); // gracefully; see crbug.com/160157. SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimit(pid_t target_pid); +// Restrict ptrace() to just read operations that are needed for crash +// reporting. See https://crbug.com/933418 for details. +SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPtrace(); + } // namespace sandbox. #endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc index 327da2bea41..d109c6186c9 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc @@ -5,14 +5,20 @@ #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" #include <errno.h> +#include <fcntl.h> +#include <linux/elf.h> #include <sched.h> +#include <sys/prctl.h> +#include <sys/ptrace.h> #include <sys/resource.h> #include <sys/syscall.h> #include <sys/types.h> +#include <sys/user.h> #include <time.h> #include <unistd.h> #include "base/bind.h" +#include "base/posix/eintr_wrapper.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/system/sys_info.h" @@ -242,6 +248,231 @@ BPF_DEATH_TEST_C(ParameterRestrictions, getrusage(RUSAGE_CHILDREN, &usage); } +// ptace() Tests /////////////////////////////////////////////////////////////// + +// Tests for ptrace involve a slightly complex setup in order to properly test +// ptrace and the variety of ways it is access-checked. The BPF_TEST macro, +// the body of which already runs in its own process, spawns another process +// called the "tracee". The "tracee" then spawns another process called the +// "tracer". The child then traces the parent and performs the test operations. +// The tracee must be careful to un-stop the tracer if the tracee expects to +// die. + +class RestrictPtracePolicy : public bpf_dsl::Policy { + public: + RestrictPtracePolicy() = default; + ~RestrictPtracePolicy() override = default; + + ResultExpr EvaluateSyscall(int sysno) const override { + switch (sysno) { + case __NR_ptrace: + return RestrictPtrace(); + default: + return Allow(); + } + } +}; + +constexpr char kExitPtraceChildClean = '!'; + +class PtraceTestHarness { + public: + using PtraceChildTracerFunc = void (*)(pid_t tracee); + + PtraceTestHarness(PtraceChildTracerFunc tracer_func, bool expect_death) + : tracer_func_(tracer_func), expect_death_(expect_death) {} + ~PtraceTestHarness() = default; + + void Run() { + // Fork the tracee process that will be traced by its child. + pid_t pid = fork(); + BPF_ASSERT_GE(pid, 0); + + if (pid == 0) { + RunTracee(); + } else { + // The tracee should always exit cleanly. + int status = 0; + int rv = waitpid(pid, &status, 0); + BPF_ASSERT_EQ(pid, rv); + BPF_ASSERT_EQ(0, WEXITSTATUS(status)); + } + } + + private: + void RunTracee() { + // Create a communications pipe between tracer and tracee. + int rv = pipe2(pipes_, O_NONBLOCK); + BPF_ASSERT_EQ(0, rv); + + // Pipes for redirecting output. + int output_pipes[2]; + BPF_ASSERT_EQ(0, pipe(output_pipes)); + + // Create the tracer process. + pid_t pid = fork(); + BPF_ASSERT_GE(pid, 0); + + if (pid == 0) { + // Close the pipe read ends and redirect output. + close(pipes_[0]); + close(output_pipes[0]); + + close(STDOUT_FILENO); + dup2(output_pipes[1], STDOUT_FILENO); + + close(STDERR_FILENO); + dup2(output_pipes[1], STDERR_FILENO); + + RunTracer(); + + close(output_pipes[1]); + } else { + close(pipes_[1]); + close(output_pipes[1]); + + // Ensure the tracer can trace the tracee. This may fail on systems + // without YAMA, so the result is not checked. + prctl(PR_SET_PTRACER, pid); + + char c = 0; + while (c != kExitPtraceChildClean) { + // Read from the control channel in a non-blocking fashion. + // If no data are present, loop. + ignore_result(read(pipes_[0], &c, 1)); + + // Poll the exit status of the child. + int status = 0; + rv = waitpid(pid, &status, WNOHANG); + if (rv != 0) { + BPF_ASSERT_EQ(pid, rv); + CheckTracerStatus(status, output_pipes[0]); + _exit(0); + } + } + + _exit(0); + } + } + + void RunTracer() { + pid_t ppid = getppid(); + BPF_ASSERT_NE(0, ppid); + + // Attach to the tracee and then call out to the test function. + BPF_ASSERT_EQ(0, ptrace(PTRACE_ATTACH, ppid, nullptr, nullptr)); + + tracer_func_(ppid); + + BPF_ASSERT_EQ(1, HANDLE_EINTR(write(pipes_[1], &kExitPtraceChildClean, 1))); + close(pipes_[1]); + + _exit(0); + } + + void CheckTracerStatus(int status, int output_pipe) { + // The child has exited. Test that it did so in the way we were + // expecting. + if (expect_death_) { + // This duplicates a bit of what //sandbox/linux/tests/unit_tests.cc does + // but that code is not shareable here. + std::string output; + const size_t kBufferSize = 1024; + size_t total_bytes_read = 0; + ssize_t read_this_pass = 0; + do { + output.resize(output.size() + kBufferSize); + read_this_pass = HANDLE_EINTR( + read(output_pipe, &output[total_bytes_read], kBufferSize)); + if (read_this_pass >= 0) { + total_bytes_read += read_this_pass; + output.resize(total_bytes_read); + } + } while (read_this_pass > 0); + +#if !defined(SANDBOX_USES_BASE_TEST_SUITE) + const bool subprocess_got_sigsegv = + WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status)); +#else + // This hack is required when a signal handler is installed + // for SEGV that will _exit(1). + const bool subprocess_got_sigsegv = + WIFEXITED(status) && (1 == WEXITSTATUS(status)); +#endif + BPF_ASSERT(subprocess_got_sigsegv); + BPF_ASSERT_NE(output.find(GetPtraceErrorMessageContentForTests()), + std::string::npos); + } else { + BPF_ASSERT(WIFEXITED(status)); + BPF_ASSERT_EQ(0, WEXITSTATUS(status)); + } + } + + PtraceChildTracerFunc tracer_func_; + bool expect_death_; + int pipes_[2]; + + DISALLOW_COPY_AND_ASSIGN(PtraceTestHarness); +}; + +BPF_TEST_C(ParameterRestrictions, + ptrace_getregs_allowed, + RestrictPtracePolicy) { + auto tracer = [](pid_t pid) { +#if defined(__arm__) + user_regs regs; +#else + user_regs_struct regs; +#endif + iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + BPF_ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, + reinterpret_cast<void*>(NT_PRSTATUS), &iov)); + + BPF_ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, nullptr, nullptr)); + }; + PtraceTestHarness(tracer, false).Run(); +} + +BPF_TEST_C(ParameterRestrictions, + ptrace_syscall_blocked, + RestrictPtracePolicy) { + auto tracer = [](pid_t pid) { + // The tracer is about to die. Make sure the tracee is not stopped so it + // can reap it and inspect its death signal. + kill(pid, SIGCONT); + + BPF_ASSERT_NE(0, ptrace(PTRACE_SYSCALL, 0, nullptr, nullptr)); + }; + PtraceTestHarness(tracer, true).Run(); +} + +BPF_TEST_C(ParameterRestrictions, + ptrace_setregs_blocked, + RestrictPtracePolicy) { + auto tracer = [](pid_t pid) { +#if defined(__arm__) + user_regs regs; +#else + user_regs_struct regs; +#endif + iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + BPF_ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, + reinterpret_cast<void*>(NT_PRSTATUS), &iov)); + + // The tracer is about to die. Make sure the tracee is not stopped so it + // can reap it and inspect its death signal. + kill(pid, SIGCONT); + + BPF_ASSERT_NE(0, ptrace(PTRACE_SETREGSET, pid, + reinterpret_cast<void*>(NT_PRSTATUS), &iov)); + }; + PtraceTestHarness(tracer, true).Run(); +} + } // namespace } // namespace sandbox diff --git a/chromium/sandbox/win/fuzzer/fuzzer_types.h b/chromium/sandbox/win/fuzzer/fuzzer_types.h deleted file mode 100644 index 8ff06d0d15c..00000000000 --- a/chromium/sandbox/win/fuzzer/fuzzer_types.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_FUZZER_TYPES_H_ -#define SANDBOX_FUZZER_TYPES_H_ - -#include <stdint.h> - -// This file defines Windows types for the sandbox_ipc_fuzzer target when -// compiled on Linux. -// -// It also disables Windows exception handling to ensure any crashes are -// captured by the fuzzing harness. - -// Disable exceptions. -#if defined(__try) -#undef __try -#endif -#define __try if(true) -#if defined(__except) -#undef __except -#endif -#define __except(...) if(false) - -// Windows types used in sandbox. -typedef void* HANDLE; -typedef uint32_t DWORD; -typedef int32_t LONG; -typedef uint32_t ULONG; -typedef uint32_t* ULONG_PTR; -typedef LONG NTSTATUS; -typedef void PROCESS_INFORMATION; - -// __stdcall is used in one place. TODO(wfh): replace with WINAPI. -#define __stdcall - -#endif // SANDBOX_FUZZER_TYPES_H_ diff --git a/chromium/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc b/chromium/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc deleted file mode 100644 index 1c2ea8c5430..00000000000 --- a/chromium/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include "sandbox/win/src/crosscall_server.h" -#include "sandbox/win/src/ipc_args.h" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - using namespace sandbox; - - uint32_t output_size = 0; - std::unique_ptr<CrossCallParamsEx> params(CrossCallParamsEx::CreateFromBuffer( - const_cast<uint8_t*>(data), size, &output_size)); - - if (!params.get()) - return 0; - - uint32_t tag = params->GetTag(); - IPCParams ipc_params = {0}; - ipc_params.ipc_tag = tag; - void* args[kMaxIpcParams]; - GetArgs(params.get(), &ipc_params, args); - ReleaseArgs(&ipc_params, args); - return 0; -} |