diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 17:21:03 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 16:25:15 +0000 |
commit | c551f43206405019121bd2b2c93714319a0a3300 (patch) | |
tree | 1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/sandbox | |
parent | 7961cea6d1041e3e454dae6a1da660b453efd238 (diff) | |
download | qtwebengine-chromium-c551f43206405019121bd2b2c93714319a0a3300.tar.gz |
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/sandbox')
37 files changed, 1088 insertions, 394 deletions
diff --git a/chromium/sandbox/constants.h b/chromium/sandbox/constants.h index 7fa11cf7e5c..3e0ebc16fe4 100644 --- a/chromium/sandbox/constants.h +++ b/chromium/sandbox/constants.h @@ -15,11 +15,11 @@ namespace sandbox { // JOBOBJECT_EXTENDED_LIMIT_INFORMATION.JobMemoryLimit on Windows. // #if defined(ARCH_CPU_64_BITS) -const size_t kDataSizeLimit = 1ULL << 32; +constexpr size_t kDataSizeLimit = size_t{1} << 34; // 16 GB #else // Limit the data memory to a size that prevents allocations that can't be // indexed by an int. -const size_t kDataSizeLimit = +constexpr size_t kDataSizeLimit = static_cast<size_t>(std::numeric_limits<int>::max()); #endif diff --git a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h index 6f0dd4eb39c..555d820e017 100644 --- a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h +++ b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h @@ -27,34 +27,32 @@ // An idiomatic and demonstrative (albeit silly) example of this API // would be: // -// #include "sandbox/linux/bpf_dsl/bpf_dsl.h" +// #include "sandbox/linux/bpf_dsl/bpf_dsl.h" // -// using namespace sandbox::bpf_dsl; +// namespace dsl = sandbox::bpf_dsl; // -// class SillyPolicy : public Policy { -// public: -// SillyPolicy() {} -// ~SillyPolicy() override {} -// ResultExpr EvaluateSyscall(int sysno) const override { -// if (sysno == __NR_fcntl) { -// Arg<int> fd(0), cmd(1); -// Arg<unsigned long> flags(2); -// const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK; -// return If(AllOf(fd == 0, -// cmd == F_SETFL, -// (flags & ~kGoodFlags) == 0), -// Allow()) -// .ElseIf(AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC), -// Error(EMFILE)) -// .Else(Trap(SetFlagHandler, NULL)); -// } else { -// return Allow(); -// } -// } +// class SillyPolicy : public dsl::Policy { +// public: +// SillyPolicy() = default; +// SillyPolicy(const SillyPolicy&) = delete; +// SillyPolicy& operator=(const SillyPolicy&) = delete; +// ~SillyPolicy() override = default; // -// private: -// DISALLOW_COPY_AND_ASSIGN(SillyPolicy); -// }; +// dsl::ResultExpr EvaluateSyscall(int sysno) const override { +// if (sysno != __NR_fcntl) +// return dsl::Allow(); +// dsl::Arg<int> fd(0), cmd(1); +// dsl::Arg<unsigned long> flags(2); +// constexpr uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK; +// return dsl::If(dsl::AllOf(fd == 0, +// cmd == F_SETFL, +// (flags & ~kGoodFlags) == 0), +// dsl::Allow()) +// .dsl::ElseIf(dsl::AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC), +// dsl::Error(EMFILE)) +// .dsl::Else(dsl::Trap(SetFlagHandler, nullptr)); +// } +// }; // // More generally, the DSL currently supports the following grammar: // diff --git a/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h b/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h index 73c26c4ba66..313511f22e9 100644 --- a/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h +++ b/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h @@ -51,8 +51,9 @@ #elif defined(__aarch64__) +#include <asm-generic/unistd.h> #define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 279u +#define MAX_PUBLIC_SYSCALL __NR_syscalls #define MAX_SYSCALL MAX_PUBLIC_SYSCALL #else diff --git a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc index 6840dba8616..55ecf588227 100644 --- a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc +++ b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc @@ -49,7 +49,7 @@ class InitializedOpenBroker { syscall_broker::BrokerFilePermission::ReadOnly("/proc/cpuinfo")}; broker_process_ = std::make_unique<syscall_broker::BrokerProcess>( EPERM, command_set, permissions); - BPF_ASSERT(broker_process_->Init(base::Bind([]() { return true; }))); + BPF_ASSERT(broker_process_->Init(base::BindOnce([]() { return true; }))); initialized_ = true; } diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android_unittest.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android_unittest.cc index e119a462de2..bc0db815abc 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android_unittest.cc @@ -33,5 +33,10 @@ BPF_TEST_C(BaselinePolicyAndroid, CanOpenProcCpuinfo, BaselinePolicyAndroid) { BPF_ASSERT_NE(-1, open("/proc/cpuinfo", O_RDONLY)); } +BPF_TEST_C(BaselinePolicyAndroid, Membarrier, BaselinePolicyAndroid) { + // Should not crash. + syscall(__NR_membarrier, 32 /* cmd */, 0 /* flags */); +} + } // namespace } // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc index 060181bd42f..b56b2944f17 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc @@ -243,7 +243,7 @@ BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) { errno = 0; char buf[1024]; char* cwd = getcwd(buf, sizeof(buf)); - BPF_ASSERT_EQ(NULL, cwd); + BPF_ASSERT_EQ(nullptr, cwd); BPF_ASSERT_EQ(EPERM, errno); } @@ -301,7 +301,7 @@ BPF_TEST_C(BaselinePolicy, FutexEINVAL, BaselinePolicy) { }; for (int op : ops) { - BPF_ASSERT_EQ(-1, syscall(__NR_futex, NULL, op, 0, NULL, NULL, 0)); + BPF_ASSERT_EQ(-1, syscall(__NR_futex, nullptr, op, 0, nullptr, nullptr, 0)); BPF_ASSERT_EQ(EINVAL, errno); } } @@ -310,7 +310,7 @@ BPF_DEATH_TEST_C(BaselinePolicy, FutexWithRequeuePriorityInheritence, DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()), BaselinePolicy) { - syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0); + syscall(__NR_futex, nullptr, FUTEX_CMP_REQUEUE_PI, 0, nullptr, nullptr, 0); _exit(1); } @@ -318,7 +318,8 @@ BPF_DEATH_TEST_C(BaselinePolicy, FutexWithRequeuePriorityInheritencePrivate, DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()), BaselinePolicy) { - syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0); + syscall(__NR_futex, nullptr, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, nullptr, + nullptr, 0); _exit(1); } @@ -326,7 +327,7 @@ BPF_DEATH_TEST_C(BaselinePolicy, FutexWithUnlockPIPrivate, DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()), BaselinePolicy) { - syscall(__NR_futex, NULL, FUTEX_UNLOCK_PI_PRIVATE, 0, NULL, NULL, 0); + syscall(__NR_futex, nullptr, FUTEX_UNLOCK_PI_PRIVATE, 0, nullptr, nullptr, 0); _exit(1); } #endif // defined(LIBC_GLIBC) && !defined(OS_CHROMEOS) @@ -390,7 +391,7 @@ BPF_DEATH_TEST_C(BaselinePolicy, BPF_DEATH_TEST_C(BaselinePolicy, ClockGettimeWithDisallowedClockCrashes, - DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), + DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { struct timespec ts; syscall(SYS_clock_gettime, CLOCK_MONOTONIC_RAW, &ts); @@ -402,15 +403,15 @@ BPF_DEATH_TEST_C(BaselinePolicy, BPF_DEATH_TEST_C(BaselinePolicy, GetRandomOfDevRandomCrashes, - DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), + DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { - syscall(__NR_getrandom, NULL, 0, GRND_RANDOM); + syscall(__NR_getrandom, nullptr, 0, GRND_RANDOM); } #if !defined(__i386__) BPF_DEATH_TEST_C(BaselinePolicy, GetSockOptWrongLevelSigsys, - DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), + DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); @@ -421,7 +422,7 @@ BPF_DEATH_TEST_C(BaselinePolicy, BPF_DEATH_TEST_C(BaselinePolicy, GetSockOptWrongOptionSigsys, - DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), + DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); @@ -432,7 +433,7 @@ BPF_DEATH_TEST_C(BaselinePolicy, BPF_DEATH_TEST_C(BaselinePolicy, SetSockOptWrongLevelSigsys, - DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), + DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); @@ -440,10 +441,9 @@ BPF_DEATH_TEST_C(BaselinePolicy, setsockopt(fds[0], IPPROTO_TCP, SO_PEEK_OFF, &id, sizeof(id)); } - BPF_DEATH_TEST_C(BaselinePolicy, SetSockOptWrongOptionSigsys, - DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), + DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); 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 f1160ff45ea..fdd954cc0fd 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 @@ -248,15 +248,9 @@ 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. +// The following ptrace() tests do not actually set up a tracer/tracee +// relationship, so allowed operations return ESRCH errors. Blocked operations +// are tested with death tests. class RestrictPtracePolicy : public bpf_dsl::Policy { public: @@ -273,208 +267,48 @@ class RestrictPtracePolicy : public bpf_dsl::Policy { } }; -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); -}; - -// Fails on Android L and M. -// See https://crbug.com/934930 BPF_TEST_C(ParameterRestrictions, - DISABLED_ptrace_getregs_allowed, + ptrace_getregs_allowed, RestrictPtracePolicy) { - auto tracer = [](pid_t pid) { #if defined(__arm__) - user_regs regs; + user_regs regs; #else - user_regs_struct regs; + 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(); + iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + errno = 0; + int rv = ptrace(PTRACE_GETREGSET, getpid(), + reinterpret_cast<void*>(NT_PRSTATUS), &iov); + BPF_ASSERT_EQ(-1, rv); + BPF_ASSERT_EQ(ESRCH, errno); } -// Fails on Android L and M. -// See https://crbug.com/934930 -BPF_TEST_C(ParameterRestrictions, - DISABLED_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_DEATH_TEST_C( + ParameterRestrictions, + ptrace_syscall_blocked, + DEATH_SEGV_MESSAGE(sandbox::GetPtraceErrorMessageContentForTests()), + RestrictPtracePolicy) { + ptrace(PTRACE_SYSCALL, getpid(), nullptr, nullptr); } -BPF_TEST_C(ParameterRestrictions, - DISABLED_ptrace_setregs_blocked, - RestrictPtracePolicy) { - auto tracer = [](pid_t pid) { +BPF_DEATH_TEST_C( + ParameterRestrictions, + ptrace_setregs_blocked, + DEATH_SEGV_MESSAGE(sandbox::GetPtraceErrorMessageContentForTests()), + RestrictPtracePolicy) { #if defined(__arm__) - user_regs regs; + user_regs regs{}; #else - user_regs_struct regs; + 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(); + iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + errno = 0; + ptrace(PTRACE_SETREGSET, getpid(), reinterpret_cast<void*>(NT_PRSTATUS), + &iov); } } // namespace diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc index 816c0d63dee..4e0ad046299 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc @@ -845,7 +845,8 @@ bool SyscallSets::IsSystemVSemaphores(int sysno) { } #endif -#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__aarch64__) || \ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) // These give a lot of ambient authority and bypass the setuid sandbox. bool SyscallSets::IsSystemVSharedMemory(int sysno) { diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h index acd92da3956..923533ec9fd 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h @@ -75,7 +75,8 @@ class SANDBOX_EXPORT SyscallSets { (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) static bool IsSystemVSemaphores(int sysno); #endif -#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__aarch64__) || \ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) // These give a lot of ambient authority and bypass the setuid sandbox. static bool IsSystemVSharedMemory(int sysno); diff --git a/chromium/sandbox/linux/services/scoped_process.cc b/chromium/sandbox/linux/services/scoped_process.cc index 65af4873a44..6ad405b6a2f 100644 --- a/chromium/sandbox/linux/services/scoped_process.cc +++ b/chromium/sandbox/linux/services/scoped_process.cc @@ -33,7 +33,7 @@ void WaitForever() { } // namespace -ScopedProcess::ScopedProcess(const base::Closure& child_callback) +ScopedProcess::ScopedProcess(base::OnceClosure child_callback) : child_process_id_(-1), process_id_(getpid()) { PCHECK(0 == pipe(pipe_fds_)); #if !defined(THREAD_SANITIZER) @@ -46,7 +46,7 @@ ScopedProcess::ScopedProcess(const base::Closure& child_callback) if (0 == child_process_id_) { PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0]))); pipe_fds_[0] = -1; - child_callback.Run(); + std::move(child_callback).Run(); // Notify the parent that the closure has run. CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1))); WaitForever(); diff --git a/chromium/sandbox/linux/services/scoped_process.h b/chromium/sandbox/linux/services/scoped_process.h index bddbd5529b0..d0f8954d611 100644 --- a/chromium/sandbox/linux/services/scoped_process.h +++ b/chromium/sandbox/linux/services/scoped_process.h @@ -24,7 +24,7 @@ class SANDBOX_EXPORT ScopedProcess { // A new process will be created and |child_callback| will run in the child // process. This callback is allowed to terminate the process or to simply // return. If the callback returns, the process will wait forever. - explicit ScopedProcess(const base::Closure& child_callback); + explicit ScopedProcess(base::OnceClosure child_callback); ~ScopedProcess(); // Wait for the process to exit. diff --git a/chromium/sandbox/linux/services/scoped_process_unittest.cc b/chromium/sandbox/linux/services/scoped_process_unittest.cc index 696e38be598..e019d6d33b1 100644 --- a/chromium/sandbox/linux/services/scoped_process_unittest.cc +++ b/chromium/sandbox/linux/services/scoped_process_unittest.cc @@ -38,7 +38,7 @@ void RaiseAndExit(int signal) { TEST(ScopedProcess, ScopedProcessNormalExit) { const int kCustomExitCode = 12; - ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode)); + ScopedProcess process(base::BindOnce(&ExitWithCode, kCustomExitCode)); bool got_signaled = true; int exit_code = process.WaitForExit(&got_signaled); EXPECT_FALSE(got_signaled); @@ -55,7 +55,7 @@ TEST(ScopedProcess, ScopedProcessNormalExit) { // Disable this test on Android, SIGABRT is funky there. TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) { PCHECK(SIG_ERR != signal(SIGABRT, SIG_DFL)); - ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT)); + ScopedProcess process(base::BindOnce(&RaiseAndExit, SIGABRT)); bool got_signaled = false; int exit_code = process.WaitForExit(&got_signaled); EXPECT_TRUE(got_signaled); @@ -77,7 +77,7 @@ TEST(ScopedProcess, DiesForReal) { base::ScopedFD read_end_closer(pipe_fds[0]); base::ScopedFD write_end_closer(pipe_fds[1]); - { ScopedProcess process(base::Bind(&DoExit)); } + { ScopedProcess process(base::BindOnce(&DoExit)); } // Close writing end of the pipe. write_end_closer.reset(); @@ -94,7 +94,7 @@ TEST(ScopedProcess, SynchronizationBasic) { ScopedProcess process1{base::DoNothing()}; EXPECT_TRUE(process1.WaitForClosureToRun()); - ScopedProcess process2(base::Bind(&DoExit)); + ScopedProcess process2(base::BindOnce(&DoExit)); // The closure didn't finish running normally. This case is simple enough // that process.WaitForClosureToRun() should return false, even though the // API does not guarantees that it will return at all. @@ -114,7 +114,7 @@ TEST(ScopedProcess, SynchronizationWorks) { // Start a process with a closure that takes a little bit to run. ScopedProcess process( - base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1])); + base::BindOnce(&SleepInMsAndWriteOneByte, 100, pipe_fds[1])); EXPECT_TRUE(process.WaitForClosureToRun()); // Verify that the closure did, indeed, run. diff --git a/chromium/sandbox/linux/services/thread_helpers.cc b/chromium/sandbox/linux/services/thread_helpers.cc index d27cfe6dcf7..9f23a0a0fa1 100644 --- a/chromium/sandbox/linux/services/thread_helpers.cc +++ b/chromium/sandbox/linux/services/thread_helpers.cc @@ -71,7 +71,8 @@ bool IsNotThreadPresentInProcFS(int proc_fd, // Debug builds (2s on Release builds). // This is guaranteed to not sleep more than twice as much as the bare minimum // amount of time. -void RunWhileTrue(const base::Callback<bool(void)>& cb, const char* message) { +void RunWhileTrue(const base::RepeatingCallback<bool(void)>& cb, + const char* message) { #if defined(NDEBUG) // In Release mode, crash after 30 iterations, which means having spent // roughly 2s in @@ -117,7 +118,7 @@ bool ChangeThreadStateAndWatchProcFS( DCHECK(thread); DCHECK(action == ThreadAction::Start || action == ThreadAction::Stop); - base::Callback<bool(void)> cb; + base::RepeatingCallback<bool(void)> cb; const char* message; if (action == ThreadAction::Start) { @@ -140,10 +141,12 @@ bool ChangeThreadStateAndWatchProcFS( // /proc. Start() above or following Stop(), the thread is started or joined, // but entries in /proc may not have been updated. if (action == ThreadAction::Start) { - cb = base::Bind(&IsNotThreadPresentInProcFS, proc_fd, thread_id_dir_str); + cb = base::BindRepeating(&IsNotThreadPresentInProcFS, proc_fd, + thread_id_dir_str); message = kAssertThreadDoesNotAppearInProcFS; } else { - cb = base::Bind(&IsThreadPresentInProcFS, proc_fd, thread_id_dir_str); + cb = base::BindRepeating(&IsThreadPresentInProcFS, proc_fd, + thread_id_dir_str); message = kAssertThreadDoesNotDisappearInProcFS; } RunWhileTrue(cb, message); @@ -171,7 +174,8 @@ bool ThreadHelpers::IsSingleThreaded() { // static void ThreadHelpers::AssertSingleThreaded(int proc_fd) { DCHECK_LE(0, proc_fd); - const base::Callback<bool(void)> cb = base::Bind(&IsMultiThreaded, proc_fd); + const base::RepeatingCallback<bool(void)> cb = + base::BindRepeating(&IsMultiThreaded, proc_fd); RunWhileTrue(cb, kAssertSingleThreadedError); } diff --git a/chromium/sandbox/linux/services/yama_unittests.cc b/chromium/sandbox/linux/services/yama_unittests.cc index 0b3817e8dcc..e693917c417 100644 --- a/chromium/sandbox/linux/services/yama_unittests.cc +++ b/chromium/sandbox/linux/services/yama_unittests.cc @@ -66,7 +66,7 @@ void ExitZeroIfCanPtrace(pid_t pid) { } bool CanSubProcessPtrace(pid_t pid) { - ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid)); + ScopedProcess process(base::BindOnce(&ExitZeroIfCanPtrace, pid)); bool signaled; int exit_code = process.WaitForExit(&signaled); CHECK(!signaled); @@ -134,7 +134,7 @@ TEST(Yama, RestrictPtraceWorks) { if (HasLinux32Bug()) return; - ScopedProcess process1(base::Bind(&SetYamaRestrictions, true)); + ScopedProcess process1(base::BindOnce(&SetYamaRestrictions, true)); ASSERT_TRUE(process1.WaitForClosureToRun()); if (Yama::IsEnforcing()) { @@ -147,7 +147,7 @@ TEST(Yama, RestrictPtraceWorks) { ASSERT_TRUE(CanPtrace(process1.GetPid())); // A sibling can ptrace process2 which disables any Yama protection. - ScopedProcess process2(base::Bind(&SetYamaRestrictions, false)); + ScopedProcess process2(base::BindOnce(&SetYamaRestrictions, false)); ASSERT_TRUE(process2.WaitForClosureToRun()); ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid())); } diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc index 56d4964cfdc..800888f9024 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_process.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc @@ -59,7 +59,7 @@ BrokerProcess::~BrokerProcess() { } bool BrokerProcess::Init( - const base::Callback<bool(void)>& broker_process_init_callback) { + base::OnceCallback<bool(void)> broker_process_init_callback) { CHECK(!initialized_); BrokerChannel::EndPoint ipc_reader; BrokerChannel::EndPoint ipc_writer; @@ -86,7 +86,7 @@ bool BrokerProcess::Init( // We are the broker process. Make sure to close the writer's end so that // we get notified if the client disappears. ipc_writer.reset(); - CHECK(broker_process_init_callback.Run()); + CHECK(std::move(broker_process_init_callback).Run()); BrokerHost broker_host(broker_permission_list_, allowed_command_set_, std::move(ipc_reader)); for (;;) { diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.h b/chromium/sandbox/linux/syscall_broker/broker_process.h index 849238f48e6..e23d9e3544a 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_process.h +++ b/chromium/sandbox/linux/syscall_broker/broker_process.h @@ -71,7 +71,7 @@ class SANDBOX_EXPORT BrokerProcess { // point, since we need to fork(). // broker_process_init_callback will be called in the new broker process, // after fork() returns. - bool Init(const base::Callback<bool(void)>& broker_process_init_callback); + bool Init(base::OnceCallback<bool(void)> broker_process_init_callback); // Return the PID of the child created by Init(). int broker_pid() const { return broker_pid_; } diff --git a/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h b/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h index a6afc62d990..7613c9bbcdc 100644 --- a/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h +++ b/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h @@ -1422,5 +1422,293 @@ #define __NR_memfd_create 356 #endif +#if !defined(__NR_bpf) +#define __NR_bpf 357 +#endif + +#if !defined(__NR_execveat) +#define __NR_execveat 358 +#endif + +#if !defined(__NR_socket) +#define __NR_socket 359 +#endif + +#if !defined(__NR_socketpair) +#define __NR_socketpair 360 +#endif + +#if !defined(__NR_bind) +#define __NR_bind 361 +#endif + +#if !defined(__NR_connect) +#define __NR_connect 362 +#endif + +#if !defined(__NR_listen) +#define __NR_listen 363 +#endif + +#if !defined(__NR_accept4) +#define __NR_accept4 364 +#endif + +#if !defined(__NR_getsockopt) +#define __NR_getsockopt 365 +#endif + +#if !defined(__NR_setsockopt) +#define __NR_setsockopt 366 +#endif + +#if !defined(__NR_getsockname) +#define __NR_getsockname 367 +#endif + +#if !defined(__NR_getpeername) +#define __NR_getpeername 368 +#endif + +#if !defined(__NR_sendto) +#define __NR_sendto 369 +#endif + +#if !defined(__NR_sendmsg) +#define __NR_sendmsg 370 +#endif + +#if !defined(__NR_recvfrom) +#define __NR_recvfrom 371 +#endif + +#if !defined(__NR_recvmsg) +#define __NR_recvmsg 372 +#endif + +#if !defined(__NR_shutdown) +#define __NR_shutdown 373 +#endif + +#if !defined(__NR_userfaultfd) +#define __NR_userfaultfd 374 +#endif + +#if !defined(__NR_membarrier) +#define __NR_membarrier 375 +#endif + +#if !defined(__NR_mlock2) +#define __NR_mlock2 376 +#endif + +#if !defined(__NR_copy_file_range) +#define __NR_copy_file_range 377 +#endif + +#if !defined(__NR_preadv2) +#define __NR_preadv2 378 +#endif + +#if !defined(__NR_pwritev2) +#define __NR_pwritev2 379 +#endif + +#if !defined(__NR_pkey_mprotect) +#define __NR_pkey_mprotect 380 +#endif + +#if !defined(__NR_pkey_alloc) +#define __NR_pkey_alloc 381 +#endif + +#if !defined(__NR_pkey_free) +#define __NR_pkey_free 382 +#endif + +#if !defined(__NR_statx) +#define __NR_statx 383 +#endif + +#if !defined(__NR_arch_prctl) +#define __NR_arch_prctl 384 +#endif + +#if !defined(__NR_io_pgetevents) +#define __NR_io_pgetevents 385 +#endif + +#if !defined(__NR_rseq) +#define __NR_rseq 386 +#endif + +#if !defined(__NR_semget) +#define __NR_semget 393 +#endif + +#if !defined(__NR_semctl) +#define __NR_semctl 394 +#endif + +#if !defined(__NR_shmget) +#define __NR_shmget 395 +#endif + +#if !defined(__NR_shmctl) +#define __NR_shmctl 396 +#endif + +#if !defined(__NR_shmat) +#define __NR_shmat 397 +#endif + +#if !defined(__NR_shmdt) +#define __NR_shmdt 398 +#endif + +#if !defined(__NR_msgget) +#define __NR_msgget 399 +#endif + +#if !defined(__NR_msgsnd) +#define __NR_msgsnd 400 +#endif + +#if !defined(__NR_msgrcv) +#define __NR_msgrcv 401 +#endif + +#if !defined(__NR_msgctl) +#define __NR_msgctl 402 +#endif + +#if !defined(__NR_clock_gettime64) +#define __NR_clock_gettime64 403 +#endif + +#if !defined(__NR_clock_settime64) +#define __NR_clock_settime64 404 +#endif + +#if !defined(__NR_clock_adjtime64) +#define __NR_clock_adjtime64 405 +#endif + +#if !defined(__NR_clock_getres_time64) +#define __NR_clock_getres_time64 406 +#endif + +#if !defined(__NR_clock_nanosleep_time64) +#define __NR_clock_nanosleep_time64 407 +#endif + +#if !defined(__NR_timer_gettime64) +#define __NR_timer_gettime64 408 +#endif + +#if !defined(__NR_timer_settime64) +#define __NR_timer_settime64 409 +#endif + +#if !defined(__NR_timerfd_gettime64) +#define __NR_timerfd_gettime64 410 +#endif + +#if !defined(__NR_timerfd_settime64) +#define __NR_timerfd_settime64 411 +#endif + +#if !defined(__NR_utimensat_time64) +#define __NR_utimensat_time64 412 +#endif + +#if !defined(__NR_pselect6_time64) +#define __NR_pselect6_time64 413 +#endif + +#if !defined(__NR_ppoll_time64) +#define __NR_ppoll_time64 414 +#endif + +#if !defined(__NR_io_pgetevents_time64) +#define __NR_io_pgetevents_time64 416 +#endif + +#if !defined(__NR_recvmmsg_time64) +#define __NR_recvmmsg_time64 417 +#endif + +#if !defined(__NR_mq_timedsend_time64) +#define __NR_mq_timedsend_time64 418 +#endif + +#if !defined(__NR_mq_timedreceive_time64) +#define __NR_mq_timedreceive_time64 419 +#endif + +#if !defined(__NR_semtimedop_time64) +#define __NR_semtimedop_time64 420 +#endif + +#if !defined(__NR_rt_sigtimedwait_time64) +#define __NR_rt_sigtimedwait_time64 421 +#endif + +#if !defined(__NR_futex_time64) +#define __NR_futex_time64 422 +#endif + +#if !defined(__NR_sched_rr_get_interval_time64) +#define __NR_sched_rr_get_interval_time64 423 +#endif + +#if !defined(__NR_pidfd_send_signal) +#define __NR_pidfd_send_signal 424 +#endif + +#if !defined(__NR_io_uring_setup) +#define __NR_io_uring_setup 425 +#endif + +#if !defined(__NR_io_uring_enter) +#define __NR_io_uring_enter 426 +#endif + +#if !defined(__NR_io_uring_register) +#define __NR_io_uring_register 427 +#endif + +#if !defined(__NR_open_tree) +#define __NR_open_tree 428 +#endif + +#if !defined(__NR_move_mount) +#define __NR_move_mount 429 +#endif + +#if !defined(__NR_fsopen) +#define __NR_fsopen 430 +#endif + +#if !defined(__NR_fsconfig) +#define __NR_fsconfig 431 +#endif + +#if !defined(__NR_fsmount) +#define __NR_fsmount 432 +#endif + +#if !defined(__NR_fspick) +#define __NR_fspick 433 +#endif + +#if !defined(__NR_pidfd_open) +#define __NR_pidfd_open 434 +#endif + +#if !defined(__NR_clone3) +#define __NR_clone3 435 +#endif + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_ diff --git a/chromium/sandbox/mac/mojom/mojom_traits_unittest.cc b/chromium/sandbox/mac/mojom/mojom_traits_unittest.cc index e6f38797d9f..dce182807a9 100644 --- a/chromium/sandbox/mac/mojom/mojom_traits_unittest.cc +++ b/chromium/sandbox/mac/mojom/mojom_traits_unittest.cc @@ -3,7 +3,8 @@ // found in the LICENSE file. #include "base/test/task_environment.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "sandbox/mac/mojom/seatbelt_extension_token_mojom_traits.h" #include "sandbox/mac/mojom/traits_test_service.mojom.h" #include "testing/gtest/include/gtest/gtest.h" @@ -14,12 +15,9 @@ namespace { class StructTraitsTest : public testing::Test, public sandbox::mac::mojom::TraitsTestService { public: - StructTraitsTest() - : interface_ptr_(), binding_(this, mojo::MakeRequest(&interface_ptr_)) {} + StructTraitsTest() : receiver_(this, remote_.BindNewPipeAndPassReceiver()) {} - sandbox::mac::mojom::TraitsTestService* interface() { - return interface_ptr_.get(); - } + sandbox::mac::mojom::TraitsTestService* interface() { return remote_.get(); } private: // TraitsTestService: @@ -31,8 +29,8 @@ class StructTraitsTest : public testing::Test, base::test::TaskEnvironment task_environment_; - sandbox::mac::mojom::TraitsTestServicePtr interface_ptr_; - mojo::Binding<sandbox::mac::mojom::TraitsTestService> binding_; + mojo::Remote<sandbox::mac::mojom::TraitsTestService> remote_; + mojo::Receiver<sandbox::mac::mojom::TraitsTestService> receiver_; }; TEST_F(StructTraitsTest, SeatbeltExtensionToken) { diff --git a/chromium/sandbox/win/BUILD.gn b/chromium/sandbox/win/BUILD.gn index 63864dcfef6..919820cd689 100644 --- a/chromium/sandbox/win/BUILD.gn +++ b/chromium/sandbox/win/BUILD.gn @@ -94,6 +94,8 @@ static_library("sandbox") { "src/restricted_token_utils.h", "src/sandbox.cc", "src/sandbox.h", + "src/sandbox_constants.cc", + "src/sandbox_constants.h", "src/sandbox_factory.h", "src/sandbox_globals.cc", "src/sandbox_nt_types.h", @@ -102,6 +104,8 @@ static_library("sandbox") { "src/sandbox_policy.h", "src/sandbox_policy_base.cc", "src/sandbox_policy_base.h", + "src/sandbox_policy_diagnostic.cc", + "src/sandbox_policy_diagnostic.h", "src/sandbox_rand.cc", "src/sandbox_rand.h", "src/sandbox_types.h", diff --git a/chromium/sandbox/win/src/acl.cc b/chromium/sandbox/win/src/acl.cc index 2f78c164069..bd0b1818332 100644 --- a/chromium/sandbox/win/src/acl.cc +++ b/chromium/sandbox/win/src/acl.cc @@ -151,4 +151,21 @@ bool AddKnownSidToObject(HANDLE object, return true; } +bool ReplacePackageSidInDacl(HANDLE object, + SE_OBJECT_TYPE object_type, + const Sid& package_sid, + ACCESS_MASK access) { + if (!AddKnownSidToObject(object, object_type, package_sid, REVOKE_ACCESS, + 0)) { + return false; + } + + Sid any_package_sid(::WinBuiltinAnyPackageSid); + if (!AddKnownSidToObject(object, object_type, any_package_sid, GRANT_ACCESS, + access)) { + return false; + } + return true; +} + } // namespace sandbox diff --git a/chromium/sandbox/win/src/acl.h b/chromium/sandbox/win/src/acl.h index 86bb92f9b11..194edb09881 100644 --- a/chromium/sandbox/win/src/acl.h +++ b/chromium/sandbox/win/src/acl.h @@ -51,6 +51,14 @@ bool AddKnownSidToObject(HANDLE object, ACCESS_MODE access_mode, ACCESS_MASK access); +// Replace package SID in DACL to the "any package" SID. It allows Low-IL +// tokens to open the object which is important for warm up when using renderer +// AppContainer. +bool ReplacePackageSidInDacl(HANDLE object, + SE_OBJECT_TYPE object_type, + const Sid& package_sid, + ACCESS_MASK access); + } // namespace sandbox #endif // SANDBOX_SRC_ACL_H_ diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc index 7c7c53397ba..bc9da0ce0ca 100644 --- a/chromium/sandbox/win/src/broker_services.cc +++ b/chromium/sandbox/win/src/broker_services.cc @@ -22,6 +22,7 @@ #include "sandbox/win/src/process_mitigations.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_policy_base.h" +#include "sandbox/win/src/sandbox_policy_diagnostic.h" #include "sandbox/win/src/target_process.h" #include "sandbox/win/src/win2k_threadpool.h" #include "sandbox/win/src/win_utils.h" @@ -50,6 +51,10 @@ sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target) { // executes TargetEventsThread(). enum { THREAD_CTRL_NONE, + THREAD_CTRL_NEW_JOB_TRACKER, + THREAD_CTRL_NEW_PROCESS_TRACKER, + THREAD_CTRL_PROCESS_SIGNALLED, + THREAD_CTRL_GET_POLICY_INFO, THREAD_CTRL_QUIT, THREAD_CTRL_LAST, }; @@ -58,8 +63,9 @@ enum { // with a job object and with a policy. struct JobTracker { JobTracker(base::win::ScopedHandle job, - scoped_refptr<sandbox::PolicyBase> policy) - : job(std::move(job)), policy(policy) {} + scoped_refptr<sandbox::PolicyBase> policy, + DWORD process_id) + : job(std::move(job)), policy(policy), process_id(process_id) {} ~JobTracker() { FreeResources(); } // Releases the Job and notifies the associated Policy object to release its @@ -68,6 +74,7 @@ struct JobTracker { base::win::ScopedHandle job; scoped_refptr<sandbox::PolicyBase> policy; + DWORD process_id; }; void JobTracker::FreeResources() { @@ -85,6 +92,61 @@ void JobTracker::FreeResources() { } } +// tracks processes that are not in jobs +struct ProcessTracker { + ProcessTracker(scoped_refptr<sandbox::PolicyBase> policy, + DWORD process_id, + base::win::ScopedHandle process) + : policy(policy), process_id(process_id), process(std::move(process)) {} + ~ProcessTracker() { FreeResources(); } + + void FreeResources(); + + scoped_refptr<sandbox::PolicyBase> policy; + DWORD process_id; + base::win::ScopedHandle process; + // Used to UnregisterWait. Not a real handle so cannot CloseHandle(). + HANDLE wait_handle; + // IOCP that is tracking this non-job process + HANDLE iocp; +}; + +void ProcessTracker::FreeResources() { + if (policy) { + policy->OnJobEmpty(nullptr); + policy = nullptr; + } +} + +// Helper redispatches process events to tracker thread. +void WINAPI ProcessEventCallback(PVOID param, BOOLEAN ignored) { + // This callback should do very little, and must be threadpool safe. + ProcessTracker* tracker = reinterpret_cast<ProcessTracker*>(param); + // If this fails we can do nothing... we will leak the policy. + ::PostQueuedCompletionStatus(tracker->iocp, 0, THREAD_CTRL_PROCESS_SIGNALLED, + reinterpret_cast<LPOVERLAPPED>(tracker)); +} + +// Helper class to send policy lists +class PolicyDiagnosticList final : public sandbox::PolicyList { + public: + PolicyDiagnosticList() {} + ~PolicyDiagnosticList() override {} + void push_back(std::unique_ptr<sandbox::PolicyInfo> info) { + internal_list_.push_back(std::move(info)); + } + std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator begin() override { + return internal_list_.begin(); + } + std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator end() override { + return internal_list_.end(); + } + size_t size() const override { return internal_list_.size(); } + + private: + std::vector<std::unique_ptr<sandbox::PolicyInfo>> internal_list_; +}; + } // namespace namespace sandbox { @@ -97,18 +159,16 @@ ResultCode BrokerServicesBase::Init() { if (job_port_.IsValid() || thread_pool_) return SBOX_ERROR_UNEXPECTED_CALL; - ::InitializeCriticalSection(&lock_); - job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0)); if (!job_port_.IsValid()) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES; no_targets_.Set(::CreateEventW(nullptr, true, false, nullptr)); job_thread_.Set(::CreateThread(nullptr, 0, // Default security and stack. TargetEventsThread, this, 0, nullptr)); if (!job_thread_.IsValid()) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES; return SBOX_ALL_OK; } @@ -135,11 +195,7 @@ BrokerServicesBase::~BrokerServicesBase() { NOTREACHED(); return; } - - tracker_list_.clear(); thread_pool_.reset(); - - ::DeleteCriticalSection(&lock_); } scoped_refptr<TargetPolicy> BrokerServicesBase::CreatePolicy() { @@ -165,6 +221,9 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { HANDLE port = broker->job_port_.Get(); HANDLE no_targets = broker->no_targets_.Get(); + std::set<DWORD> child_process_ids; + std::list<std::unique_ptr<JobTracker>> jobs; + std::list<std::unique_ptr<ProcessTracker>> processes; int target_counter = 0; int untracked_target_counter = 0; ::ResetEvent(no_targets); @@ -189,23 +248,25 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { switch (events) { case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { // The job object has signaled that the last process associated - // with it has terminated. Assuming there is no way for a process - // to appear out of thin air in this job, it safe to assume that - // we can tell the policy to destroy the target object, and for - // us to release our reference to the policy object. - tracker->FreeResources(); + // with it has terminated. It is safe to free the tracker + // and release its reference to the associated policy object + // which will Close the job handle. + HANDLE job_handle = tracker->job.Get(); + + // Erase by comparing with the job handle. + jobs.erase(std::remove_if( + jobs.begin(), jobs.end(), + [&](auto&& p) -> bool { return p->job.Get() == job_handle; })); break; } case JOB_OBJECT_MSG_NEW_PROCESS: { - DWORD handle = static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)); - { - AutoLock lock(&broker->lock_); - size_t count = broker->child_process_ids_.count(handle); - // Child process created from sandboxed process. - if (count == 0) - untracked_target_counter++; - } + // Child process created from sandboxed process. + DWORD process_id = + static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)); + size_t count = child_process_ids.count(process_id); + if (count == 0) + untracked_target_counter++; ++target_counter; if (1 == target_counter) { ::ResetEvent(no_targets); @@ -215,12 +276,8 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { case JOB_OBJECT_MSG_EXIT_PROCESS: case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: { - size_t erase_result = 0; - { - AutoLock lock(&broker->lock_); - erase_result = broker->child_process_ids_.erase( - static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl))); - } + size_t erase_result = child_process_ids.erase( + static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl))); if (erase_result != 1U) { // The process was untracked e.g. a child process of the target. --untracked_target_counter; @@ -254,8 +311,75 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { break; } } + } else if (THREAD_CTRL_NEW_JOB_TRACKER == key) { + std::unique_ptr<JobTracker> tracker; + tracker.reset(reinterpret_cast<JobTracker*>(ovl)); + DCHECK(tracker->job.IsValid()); + + child_process_ids.insert(tracker->process_id); + jobs.push_back(std::move(tracker)); + + } else if (THREAD_CTRL_NEW_PROCESS_TRACKER == key) { + std::unique_ptr<ProcessTracker> tracker; + tracker.reset(reinterpret_cast<ProcessTracker*>(ovl)); + + if (child_process_ids.empty()) { + ::SetEvent(broker->no_targets_.Get()); + } + + tracker->iocp = port; + if (!::RegisterWaitForSingleObject(&(tracker->wait_handle), + tracker->process.Get(), + ProcessEventCallback, tracker.get(), + INFINITE, WT_EXECUTEONLYONCE)) { + // Failed. Invalidate the wait_handle and store anyway. + tracker->wait_handle = INVALID_HANDLE_VALUE; + } + processes.push_back(std::move(tracker)); + + } else if (THREAD_CTRL_PROCESS_SIGNALLED == key) { + ProcessTracker* tracker = + static_cast<ProcessTracker*>(reinterpret_cast<void*>(ovl)); + + ::UnregisterWait(tracker->wait_handle); + tracker->wait_handle = INVALID_HANDLE_VALUE; + + // PID is unique until the process handle is closed in dtor. + processes.erase(std::remove_if( + processes.begin(), processes.end(), [&](auto&& p) -> bool { + return p->process_id == tracker->process_id; + })); + + } else if (THREAD_CTRL_GET_POLICY_INFO == key) { + // Clone the policies for sandbox diagnostics. + std::unique_ptr<PolicyDiagnosticsReceiver> receiver; + receiver.reset(static_cast<PolicyDiagnosticsReceiver*>( + reinterpret_cast<void*>(ovl))); + // The PollicyInfo ctor copies essential information from the trackers. + auto policy_list = std::make_unique<PolicyDiagnosticList>(); + for (auto&& process_tracker : processes) { + if (process_tracker->policy) { + policy_list->push_back(std::make_unique<PolicyDiagnostic>( + process_tracker->policy.get())); + } + } + for (auto&& job_tracker : jobs) { + if (job_tracker->policy) { + policy_list->push_back( + std::make_unique<PolicyDiagnostic>(job_tracker->policy.get())); + } + } + // Receiver should return quickly. + receiver->ReceiveDiagnostics(std::move(policy_list)); + } else if (THREAD_CTRL_QUIT == key) { // The broker object is being destroyed so the thread needs to exit. + for (auto&& tracker : processes) { + ::UnregisterWait(tracker->wait_handle); + tracker->wait_handle = INVALID_HANDLE_VALUE; + } + // After this point, so further calls to ProcessEventCallback can + // occur. Other tracked objects are destroyed as this thread ends. return 0; } else { // We have not implemented more commands. @@ -290,8 +414,6 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, DCHECK(thread_id == ::GetCurrentThreadId()); *last_warning = SBOX_ALL_OK; - AutoLock lock(&lock_); - // Launcher thread only needs to be opted out of ACG once. Do this on the // first child process being spawned. static bool launcher_thread_opted_out = false; @@ -493,32 +615,39 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, return result; } - // We are going to keep a pointer to the policy because we'll call it when - // the job object generates notifications using the completion port. if (job.IsValid()) { - std::unique_ptr<JobTracker> tracker = - std::make_unique<JobTracker>(std::move(job), policy_base); - + JobTracker* tracker = + new JobTracker(std::move(job), policy_base, process_info.process_id()); + + // Post the tracker to the tracking thread, then associate the job with + // the tracker. The worker thread takes ownership of these objects. + CHECK(::PostQueuedCompletionStatus( + job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER, + reinterpret_cast<LPOVERLAPPED>(tracker))); // There is no obvious recovery after failure here. Previous version with // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639 - CHECK(AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), - tracker.get())); - - // Save the tracker because in cleanup we might need to force closing - // the Jobs. - tracker_list_.push_back(std::move(tracker)); - child_process_ids_.insert(process_info.process_id()); + CHECK( + AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), tracker)); } else { - // Leak policy_base. This needs to outlive the child process, but there's - // nothing that tracks that lifetime properly if there's no job object. - // TODO(wfh): Find a way to make this have the correct lifetime. - policy_base->AddRef(); - - // We have to signal the event once here because the completion port will - // never get a message that this target is being terminated thus we should - // not block WaitForAllTargets until we have at least one target with job. - if (child_process_ids_.empty()) - ::SetEvent(no_targets_.Get()); + // Duplicate the process handle to give the tracking machinery + // something valid to wait on in the tracking thread. + HANDLE tmp_process_handle = INVALID_HANDLE_VALUE; + if (!::DuplicateHandle(::GetCurrentProcess(), process_info.process_handle(), + ::GetCurrentProcess(), &tmp_process_handle, + SYNCHRONIZE, false, 0 /*no options*/)) { + *last_error = ::GetLastError(); + // This may fail in the same way as Job associated processes. + // crbug.com/480639. + SpawnCleanup(target); + return SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE; + } + base::win::ScopedHandle dup_process_handle(tmp_process_handle); + ProcessTracker* tracker = new ProcessTracker( + policy_base, process_info.process_id(), std::move(dup_process_handle)); + // The tracker and policy will leak if this call fails. + ::PostQueuedCompletionStatus(job_port_.Get(), 0, + THREAD_CTRL_NEW_PROCESS_TRACKER, + reinterpret_cast<LPOVERLAPPED>(tracker)); } *target_info = process_info.Take(); @@ -530,4 +659,20 @@ ResultCode BrokerServicesBase::WaitForAllTargets() { return SBOX_ALL_OK; } +ResultCode BrokerServicesBase::GetPolicyDiagnostics( + std::unique_ptr<PolicyDiagnosticsReceiver> receiver) { + CHECK(job_thread_.IsValid()); + // Post to the job thread. + if (!::PostQueuedCompletionStatus( + job_port_.Get(), 0, THREAD_CTRL_GET_POLICY_INFO, + reinterpret_cast<LPOVERLAPPED>(receiver.get()))) { + receiver->OnError(SBOX_ERROR_GENERIC); + return SBOX_ERROR_GENERIC; + } + + // Ownership has passed to tracker thread. + receiver.release(); + return SBOX_ALL_OK; +} + } // namespace sandbox diff --git a/chromium/sandbox/win/src/broker_services.h b/chromium/sandbox/win/src/broker_services.h index 701d29a40d1..c268b074efe 100644 --- a/chromium/sandbox/win/src/broker_services.h +++ b/chromium/sandbox/win/src/broker_services.h @@ -22,12 +22,6 @@ #include "sandbox/win/src/win2k_threadpool.h" #include "sandbox/win/src/win_utils.h" -namespace { - -struct JobTracker; - -} // namespace - namespace sandbox { // BrokerServicesBase --------------------------------------------------------- @@ -54,6 +48,8 @@ class BrokerServicesBase final : public BrokerServices, DWORD* last_error, PROCESS_INFORMATION* target) override; ResultCode WaitForAllTargets() override; + ResultCode GetPolicyDiagnostics( + std::unique_ptr<PolicyDiagnosticsReceiver> receiver) override; private: // The routine that the worker thread executes. It is in charge of @@ -71,20 +67,9 @@ class BrokerServicesBase final : public BrokerServices, // Handle to the worker thread that reacts to job notifications. base::win::ScopedHandle job_thread_; - // Lock used to protect the list of targets from being modified by 2 - // threads at the same time. - CRITICAL_SECTION lock_; - // Provides a pool of threads that are used to wait on the IPC calls. std::unique_ptr<ThreadProvider> thread_pool_; - // List of the trackers for closing and cleanup purposes. - std::list<std::unique_ptr<JobTracker>> tracker_list_; - - // Provides a fast lookup to identify sandboxed processes that belong to a - // job. - std::set<DWORD> child_process_ids_; - DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase); }; diff --git a/chromium/sandbox/win/src/handle_closer_agent.cc b/chromium/sandbox/win/src/handle_closer_agent.cc index 65410b19815..a0f12a52f4f 100644 --- a/chromium/sandbox/win/src/handle_closer_agent.cc +++ b/chromium/sandbox/win/src/handle_closer_agent.cc @@ -8,6 +8,7 @@ #include <stddef.h> #include "base/logging.h" +#include "base/win/static_constants.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/win_utils.h" @@ -173,7 +174,7 @@ bool HandleCloserAgent::CloseHandles() { // Skip closing these handles when Application Verifier is in use in order to // avoid invalid-handle exceptions. - if (GetModuleHandleW(L"vrfcore.dll")) + if (GetModuleHandleA(base::win::kApplicationVerifierDllName)) return true; // Set up buffers for the type info and the name. diff --git a/chromium/sandbox/win/src/process_mitigations_unittest.cc b/chromium/sandbox/win/src/process_mitigations_unittest.cc index 81f96451882..a4d404db582 100644 --- a/chromium/sandbox/win/src/process_mitigations_unittest.cc +++ b/chromium/sandbox/win/src/process_mitigations_unittest.cc @@ -9,6 +9,7 @@ #include "base/files/file_util.h" #include "base/numerics/safe_conversions.h" #include "base/path_service.h" +#include "base/process/kill.h" #include "base/scoped_native_library.h" #include "base/test/test_timeouts.h" #include "base/win/windows_version.h" @@ -878,6 +879,11 @@ TEST(ProcessMitigationsTest, MAYBE_CheckWin10MsSigned_FailurePreSpawn) { ScopedTestMutex mutex(hooking_dll::g_hooking_dll_mutex); + // Other code in base/process relies on this invariant. + static_assert( + base::win::kStatusInvalidImageHashExitCode == STATUS_INVALID_IMAGE_HASH, + "Invalid hash exit code does not match between base and sandbox."); + #if defined(COMPONENT_BUILD) // In a component build, the executable will fail to start-up because // imports e.g. base.dll cannot be resolved. diff --git a/chromium/sandbox/win/src/restricted_token_unittest.cc b/chromium/sandbox/win/src/restricted_token_unittest.cc index d8798e9e0a4..88cae12e3d5 100644 --- a/chromium/sandbox/win/src/restricted_token_unittest.cc +++ b/chromium/sandbox/win/src/restricted_token_unittest.cc @@ -11,6 +11,7 @@ #include "base/win/atl.h" #include "base/win/scoped_handle.h" #include "base/win/windows_version.h" +#include "sandbox/win/src/acl.h" #include "sandbox/win/src/security_capabilities.h" #include "sandbox/win/src/sid.h" #include "testing/gtest/include/gtest/gtest.h" @@ -84,6 +85,46 @@ bool GetVariableTokenInformation(const base::win::ScopedHandle& token, information); } +void CheckDaclForPackageSid(const base::win::ScopedHandle& token, + PSECURITY_CAPABILITIES security_capabilities, + bool package_sid_required) { + DWORD length_needed = 0; + ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0, + &length_needed); + ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER}); + + std::vector<char> security_desc_buffer(length_needed); + SECURITY_DESCRIPTOR* security_desc = + reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data()); + + ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, + security_desc, length_needed, + &length_needed)); + + ATL::CSecurityDesc token_sd(*security_desc); + ATL::CDacl dacl; + ASSERT_TRUE(token_sd.GetDacl(&dacl)); + + ATL::CSid package_sid( + static_cast<SID*>(security_capabilities->AppContainerSid)); + ATL::CSid all_package_sid( + static_cast<SID*>(sandbox::Sid(::WinBuiltinAnyPackageSid).GetPSID())); + + unsigned int ace_count = dacl.GetAceCount(); + for (unsigned int i = 0; i < ace_count; ++i) { + ATL::CSid sid; + ACCESS_MASK mask = 0; + BYTE type = 0; + dacl.GetAclEntry(i, &sid, &mask, &type); + if (mask != TOKEN_ALL_ACCESS || type != ACCESS_ALLOWED_ACE_TYPE) + continue; + if (sid == package_sid) + EXPECT_TRUE(package_sid_required); + else if (sid == all_package_sid) + EXPECT_FALSE(package_sid_required); + } +} + void CheckLowBoxToken(const base::win::ScopedHandle& token, TOKEN_TYPE token_type, PSECURITY_CAPABILITIES security_capabilities) { @@ -126,38 +167,7 @@ void CheckLowBoxToken(const base::win::ScopedHandle& token, security_capabilities->Capabilities[index].Sid)); } - DWORD length_needed = 0; - ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0, - &length_needed); - ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER}); - - std::vector<char> security_desc_buffer(length_needed); - SECURITY_DESCRIPTOR* security_desc = - reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data()); - - ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, - security_desc, length_needed, - &length_needed)); - - ATL::CSecurityDesc token_sd(*security_desc); - ATL::CSid check_sid( - static_cast<SID*>(security_capabilities->AppContainerSid)); - bool package_sid_found = false; - - ATL::CDacl dacl; - ASSERT_TRUE(token_sd.GetDacl(&dacl)); - unsigned int ace_count = dacl.GetAceCount(); - for (unsigned int i = 0; i < ace_count; ++i) { - ATL::CSid sid; - ACCESS_MASK mask = 0; - BYTE type = 0; - dacl.GetAclEntry(i, &sid, &mask, &type); - if (sid == check_sid && mask == TOKEN_ALL_ACCESS && - type == ACCESS_ALLOWED_ACE_TYPE) { - package_sid_found = true; - } - } - ASSERT_TRUE(package_sid_found); + CheckDaclForPackageSid(token, security_capabilities, true); } // Checks if a sid is in the restricting list of the restricted token. @@ -761,6 +771,11 @@ TEST(RestrictedTokenTest, LowBoxToken) { ASSERT_TRUE(token.IsValid()); CheckLowBoxToken(token, ::TokenPrimary, &caps_no_capabilities); + ASSERT_TRUE(ReplacePackageSidInDacl(token.Get(), SE_KERNEL_OBJECT, + Sid(caps_no_capabilities.AppContainerSid), + TOKEN_ALL_ACCESS)); + CheckDaclForPackageSid(token, &caps_no_capabilities, false); + ASSERT_EQ(DWORD{ERROR_SUCCESS}, CreateLowBoxToken(nullptr, IMPERSONATION, &caps_no_capabilities, nullptr, 0, &token)); diff --git a/chromium/sandbox/win/src/restricted_token_utils.cc b/chromium/sandbox/win/src/restricted_token_utils.cc index 53f86e07d87..496fb37272d 100644 --- a/chromium/sandbox/win/src/restricted_token_utils.cc +++ b/chromium/sandbox/win/src/restricted_token_utils.cc @@ -368,7 +368,8 @@ DWORD CreateLowBoxToken(HANDLE base_token, &token_lowbox, base_token, TOKEN_ALL_ACCESS, &obj_attr, security_capabilities->AppContainerSid, security_capabilities->CapabilityCount, - security_capabilities->Capabilities, saved_handles_count, saved_handles); + security_capabilities->Capabilities, saved_handles_count, + saved_handles_count > 0 ? saved_handles : nullptr); if (!NT_SUCCESS(status)) return GetLastErrorFromNtStatus(status); diff --git a/chromium/sandbox/win/src/sandbox.h b/chromium/sandbox/win/src/sandbox.h index f8b7c8f6f63..9dfebfcc172 100644 --- a/chromium/sandbox/win/src/sandbox.h +++ b/chromium/sandbox/win/src/sandbox.h @@ -25,6 +25,10 @@ #include "sandbox/win/fuzzer/fuzzer_types.h" #endif +#include <stddef.h> +#include <memory> +#include <vector> + #include "base/memory/ref_counted.h" #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/sandbox_types.h" @@ -33,6 +37,7 @@ namespace sandbox { class BrokerServices; +class PolicyDiagnosticsReceiver; class ProcessState; class TargetPolicy; class TargetServices; @@ -96,6 +101,18 @@ class BrokerServices { // more information. virtual ResultCode WaitForAllTargets() = 0; + // This call creates a snapshot of policies managed by the sandbox and + // returns them via a helper class. + // Parameters: + // receiver: The |PolicyDiagnosticsReceiver| implementation will be + // called to accept the results of the call. + // Returns: + // ALL_OK if the request was dispatched. All other return values + // imply failure, and the responder will not receive its completion + // callback. + virtual ResultCode GetPolicyDiagnostics( + std::unique_ptr<PolicyDiagnosticsReceiver> receiver) = 0; + protected: ~BrokerServices() {} }; @@ -145,6 +162,36 @@ class TargetServices { ~TargetServices() {} }; +class PolicyInfo { + public: + // Returns a JSON representation of the policy snapshot. + // This pointer has the same lifetime as this PolicyInfo object. + virtual const char* JsonString() = 0; + virtual ~PolicyInfo() {} +}; + +// This is returned by BrokerServices::GetPolicyDiagnostics(). +// PolicyInfo entries need not be ordered. +class PolicyList { + public: + virtual std::vector<std::unique_ptr<PolicyInfo>>::iterator begin() = 0; + virtual std::vector<std::unique_ptr<PolicyInfo>>::iterator end() = 0; + virtual size_t size() const = 0; + virtual ~PolicyList() {} +}; + +// This class mediates calls to BrokerServices::GetPolicyDiagnostics(). +class PolicyDiagnosticsReceiver { + public: + // ReceiveDiagnostics() should return quickly and should not block the + // thread on which it is called. + virtual void ReceiveDiagnostics(std::unique_ptr<PolicyList> policies) = 0; + // OnError() is passed any errors encountered and |ReceiveDiagnostics| + // will not be called. + virtual void OnError(ResultCode code) = 0; + virtual ~PolicyDiagnosticsReceiver() {} +}; + } // namespace sandbox #endif // SANDBOX_WIN_SRC_SANDBOX_H_ diff --git a/chromium/sandbox/win/src/sandbox_constants.cc b/chromium/sandbox/win/src/sandbox_constants.cc new file mode 100644 index 00000000000..1d932427d13 --- /dev/null +++ b/chromium/sandbox/win/src/sandbox_constants.cc @@ -0,0 +1,17 @@ +// Copyright 2019 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 "sandbox/win/src/sandbox_constants.h" + +namespace sandbox { +// Strings used as keys in base::Value snapshots of Policies for WebUI. +extern const char kAppContainerSid[] = "appContainerSid"; +extern const char kDesiredIntegrityLevel[] = "desiredIntegrityLevel"; +extern const char kDesiredMitigations[] = "desiredMitigations"; +extern const char kJobLevel[] = "jobLevel"; +extern const char kLockdownLevel[] = "lockdownLevel"; +extern const char kLowboxSid[] = "lowboxSid"; +extern const char kPlatformMitigations[] = "platformMitigations"; +extern const char kProcessIds[] = "processIds"; +} // namespace sandbox diff --git a/chromium/sandbox/win/src/sandbox_constants.h b/chromium/sandbox/win/src/sandbox_constants.h new file mode 100644 index 00000000000..baf9a1e48d4 --- /dev/null +++ b/chromium/sandbox/win/src/sandbox_constants.h @@ -0,0 +1,21 @@ +// Copyright 2019 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_WIN_SRC_SANDBOX_CONSTANTS_H_ +#define SANDBOX_WIN_SRC_SANDBOX_CONSTANTS_H_ + +namespace sandbox { +// Strings used as keys in base::Value snapshots of Policies. +extern const char kAppContainerSid[]; +extern const char kDesiredIntegrityLevel[]; +extern const char kDesiredMitigations[]; +extern const char kJobLevel[]; +extern const char kLockdownLevel[]; +extern const char kLowboxSid[]; +extern const char kPlatformMitigations[]; +extern const char kProcessIds[]; + +} // namespace sandbox + +#endif // SANDBOX_WIN_SRC_SANDBOX_CONSTANTS_H_ diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc index dc4375b3f36..ab27faf874f 100644 --- a/chromium/sandbox/win/src/sandbox_policy_base.cc +++ b/chromium/sandbox/win/src/sandbox_policy_base.cc @@ -15,6 +15,7 @@ #include "base/strings/stringprintf.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" +#include "sandbox/win/src/acl.h" #include "sandbox/win/src/filesystem_policy.h" #include "sandbox/win/src/interception.h" #include "sandbox/win/src/job.h" @@ -299,7 +300,7 @@ ResultCode PolicyBase::SetLowBox(const wchar_t* sid) { return SBOX_ERROR_BAD_PARAMS; if (!ConvertStringSidToSid(sid, &lowbox_sid_)) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_INVALID_LOWBOX_SID; return SBOX_ALL_OK; } @@ -403,7 +404,7 @@ ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) { DWORD result = job_obj.Init(job_level_, nullptr, ui_exceptions_, memory_limit_); if (ERROR_SUCCESS != result) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_INIT_JOB; *job = job_obj.Take(); return SBOX_ALL_OK; @@ -418,7 +419,7 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, CreateRestrictedToken(effective_token_, lockdown_level_, integrity_level_, PRIMARY, lockdown_default_dacl_, lockdown); if (ERROR_SUCCESS != result) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN; // If we're launching on the alternate desktop we need to make sure the // integrity label on the object is no higher than the sandboxed process's @@ -444,7 +445,7 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, SetObjectIntegrityLabel(desktop_handle, SE_WINDOW_OBJECT, L"", GetIntegrityLevelString(integrity_level_)); if (ERROR_SUCCESS != result) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY; if (use_alternate_winstation_) { alternate_desktop_integrity_level_label_ = integrity_level_; @@ -471,7 +472,12 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, SecurityCapabilities caps(package_sid); if (CreateLowBoxToken(lockdown->Get(), PRIMARY, &caps, saved_handles, saved_handles_count, lowbox) != ERROR_SUCCESS) { - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN; + } + + if (!ReplacePackageSidInDacl(lowbox->Get(), SE_KERNEL_OBJECT, package_sid, + TOKEN_ALL_ACCESS)) { + return SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL; } } @@ -482,7 +488,7 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, CreateRestrictedToken(effective_token_, initial_level_, integrity_level_, IMPERSONATION, lockdown_default_dacl_, initial); if (ERROR_SUCCESS != result) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN; return SBOX_ALL_OK; } @@ -753,17 +759,30 @@ ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem, break; } case SUBSYS_WIN32K_LOCKDOWN: { - if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules( - pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; + // Win32k intercept rules only supported on Windows 8 and above. This must + // match the version checks in process_mitigations.cc for consistency. + if (base::win::GetVersion() >= base::win::Version::WIN8) { + DCHECK_EQ(MITIGATION_WIN32K_DISABLE, + mitigations_ & MITIGATION_WIN32K_DISABLE) + << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy " + "rules."; + if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules( + pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } } break; } case SUBSYS_SIGNED_BINARY: { - // These rules only need to be added if the - // MITIGATION_FORCE_MS_SIGNED_BINS pre-startup mitigation is set. - if (mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS) { + // Signed intercept rules only supported on Windows 10 TH2 and above. This + // must match the version checks in process_mitigations.cc for + // consistency. + if (base::win::GetVersion() >= base::win::Version::WIN10_TH2) { + DCHECK_EQ(MITIGATION_FORCE_MS_SIGNED_BINS, + mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS) + << "Enable MITIGATION_FORCE_MS_SIGNED_BINS before adding signed " + "policy rules."; if (!SignedPolicy::GenerateRules(pattern, semantics, policy_maker_)) { NOTREACHED(); return SBOX_ERROR_BAD_PARAMS; diff --git a/chromium/sandbox/win/src/sandbox_policy_base.h b/chromium/sandbox/win/src/sandbox_policy_base.h index 631f974df70..03a8948b789 100644 --- a/chromium/sandbox/win/src/sandbox_policy_base.h +++ b/chromium/sandbox/win/src/sandbox_policy_base.h @@ -32,6 +32,7 @@ namespace sandbox { class LowLevelPolicy; +class PolicyDiagnostic; class TargetProcess; struct PolicyGlobal; @@ -113,6 +114,8 @@ class PolicyBase final : public TargetPolicy { const base::HandlesToInheritVector& GetHandlesBeingShared(); private: + // Allow PolicyInfo to snapshot PolicyBase for diagnostics. + friend class PolicyDiagnostic; ~PolicyBase(); // Sets up interceptions for a new target. diff --git a/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc b/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc new file mode 100644 index 00000000000..190b7296ea0 --- /dev/null +++ b/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc @@ -0,0 +1,197 @@ +// Copyright 2019 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 "sandbox/win/src/sandbox_policy_diagnostic.h" + +#include <stddef.h> + +#include <cinttypes> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/json/json_string_value_serializer.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/values.h" +#include "sandbox/win/src/sandbox_constants.h" +#include "sandbox/win/src/sandbox_policy_base.h" +#include "sandbox/win/src/target_process.h" +#include "sandbox/win/src/win_utils.h" + +namespace sandbox { + +namespace { + +base::Value ProcessIdList(std::vector<uint32_t> process_ids) { + base::Value results(base::Value::Type::LIST); + for (const auto pid : process_ids) { + results.GetList().push_back(base::Value(base::strict_cast<double>(pid))); + } + return results; +} + +std::string GetTokenLevelInEnglish(TokenLevel token) { + switch (token) { + case USER_LOCKDOWN: + return "Lockdown"; + case USER_RESTRICTED: + return "Restricted"; + case USER_LIMITED: + return "Limited"; + case USER_INTERACTIVE: + return "Interactive"; + case USER_NON_ADMIN: + return "Non Admin"; + case USER_RESTRICTED_SAME_ACCESS: + return "Restricted Same Access"; + case USER_UNPROTECTED: + return "Unprotected"; + case USER_RESTRICTED_NON_ADMIN: + return "Restricted Non Admin"; + case USER_LAST: + default: + DCHECK(false) << "Unknown TokenType"; + return "Unknown"; + } +} + +std::string GetJobLevelInEnglish(JobLevel job) { + switch (job) { + case JOB_LOCKDOWN: + return "Lockdown"; + case JOB_RESTRICTED: + return "Restricted"; + case JOB_LIMITED_USER: + return "Limited User"; + case JOB_INTERACTIVE: + return "Interactive"; + case JOB_UNPROTECTED: + return "Unprotected"; + case JOB_NONE: + return "None"; + default: + DCHECK(false) << "Unknown JobLevel"; + return "Unknown"; + } +} + +std::string GetIntegrityLevelInEnglish(IntegrityLevel integrity) { + switch (integrity) { + case INTEGRITY_LEVEL_SYSTEM: + return "S-1-16-16384 System"; + case INTEGRITY_LEVEL_HIGH: + return "S-1-16-12288 High"; + case INTEGRITY_LEVEL_MEDIUM: + return "S-1-16-8192 Medium"; + case INTEGRITY_LEVEL_MEDIUM_LOW: + return "S-1-16-6144 Medium Low"; + case INTEGRITY_LEVEL_LOW: + return "S-1-16-4096 Low"; + case INTEGRITY_LEVEL_BELOW_LOW: + return "S-1-16-2048 Below Low"; + case INTEGRITY_LEVEL_UNTRUSTED: + return "S-1-16-0 Untrusted"; + case INTEGRITY_LEVEL_LAST: + return "Default"; + default: + DCHECK(false) << "Unknown IntegrityLevel"; + return "Unknown"; + } +} + +base::string16 GetSidAsString(const Sid* sid) { + base::string16 result; + if (!sid->ToSddlString(&result)) + DCHECK(false) << "Failed to make sddl string"; + return result; +} + +std::string GetMitigationsAsHex(MitigationFlags mitigations) { + return base::StringPrintf("%016" PRIx64, + base::checked_cast<uint64_t>(mitigations)); +} + +std::string GetPlatformMitigationsAsHex(MitigationFlags mitigations) { + DWORD64 platform_flags[2] = {0}; + size_t flags_size = 0; + sandbox::ConvertProcessMitigationsToPolicy(mitigations, &(platform_flags[0]), + &flags_size); + DCHECK(flags_size / sizeof(DWORD64) <= 2) + << "Unexpected mitigation flags size"; + if (flags_size == 2 * sizeof(DWORD64)) + return base::StringPrintf("%016" PRIx64 "%016" PRIx64, platform_flags[0], + platform_flags[1]); + return base::StringPrintf("%016" PRIx64, platform_flags[0]); +} + +} // namespace + +// We are a friend of PolicyBase so that we can steal its private members +// quickly in the BrokerServices tracker thread. +PolicyDiagnostic::PolicyDiagnostic(PolicyBase* policy) { + DCHECK(policy); + // TODO(crbug/997273) Add more fields once webui plumbing is complete. + { + AutoLock lock(&policy->lock_); + for (auto&& target_process : policy->targets_) { + process_ids_.push_back( + base::strict_cast<uint32_t>(target_process->ProcessId())); + } + } + lockdown_level_ = policy->lockdown_level_; + job_level_ = policy->job_level_; + + // Select the final integrity level. + if (policy->delayed_integrity_level_ == INTEGRITY_LEVEL_LAST) + desired_integrity_level_ = policy->integrity_level_; + else + desired_integrity_level_ = policy->delayed_integrity_level_; + + desired_mitigations_ = policy->mitigations_ | policy->delayed_mitigations_; + + if (policy->app_container_profile_) + app_container_sid_ = + std::make_unique<Sid>(policy->app_container_profile_->GetPackageSid()); + if (policy->lowbox_sid_) + lowbox_sid_ = std::make_unique<Sid>(policy->lowbox_sid_); +} + +PolicyDiagnostic::~PolicyDiagnostic() = default; + +const char* PolicyDiagnostic::JsonString() { + // Lazily constructs json_string_. + if (json_string_) + return json_string_->c_str(); + + base::Value value(base::Value::Type::DICTIONARY); + value.SetKey(kProcessIds, ProcessIdList(process_ids_)); + value.SetKey(kLockdownLevel, + base::Value(GetTokenLevelInEnglish(lockdown_level_))); + value.SetKey(kJobLevel, base::Value(GetJobLevelInEnglish(job_level_))); + value.SetKey( + kDesiredIntegrityLevel, + base::Value(GetIntegrityLevelInEnglish(desired_integrity_level_))); + value.SetKey(kDesiredMitigations, + base::Value(GetMitigationsAsHex(desired_mitigations_))); + value.SetKey(kPlatformMitigations, + base::Value(GetPlatformMitigationsAsHex(desired_mitigations_))); + + if (app_container_sid_) + value.SetKey(kAppContainerSid, + base::Value(GetSidAsString(app_container_sid_.get()))); + + if (lowbox_sid_) + value.SetKey(kLowboxSid, base::Value(GetSidAsString(lowbox_sid_.get()))); + + auto json_string = std::make_unique<std::string>(); + JSONStringValueSerializer to_json(json_string.get()); + CHECK(to_json.Serialize(value)); + json_string_ = std::move(json_string); + return json_string_->c_str(); +} + +} // namespace sandbox diff --git a/chromium/sandbox/win/src/sandbox_policy_diagnostic.h b/chromium/sandbox/win/src/sandbox_policy_diagnostic.h new file mode 100644 index 00000000000..0fb4da7bfd7 --- /dev/null +++ b/chromium/sandbox/win/src/sandbox_policy_diagnostic.h @@ -0,0 +1,51 @@ +// Copyright 2019 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_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_ +#define SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_ + +#include <stddef.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/values.h" +#include "sandbox/win/src/process_mitigations.h" +#include "sandbox/win/src/sandbox.h" +#include "sandbox/win/src/security_level.h" +#include "sandbox/win/src/sid.h" + +namespace sandbox { + +class PolicyBase; + +// Intended to rhyme with TargetPolicy, may eventually share a common base +// with a configuration holding class (i.e. this class will extend with dynamic +// members such as the |process_ids_| list.) +class PolicyDiagnostic final : public PolicyInfo { + public: + // This should quickly copy what it needs from PolicyBase. + PolicyDiagnostic(PolicyBase* policy); + ~PolicyDiagnostic() override; + const char* JsonString() override; + + private: + // |json_string_| is lazily constructed. + std::unique_ptr<std::string> json_string_; + std::vector<uint32_t> process_ids_; + TokenLevel lockdown_level_ = USER_LAST; + JobLevel job_level_ = JOB_NONE; + IntegrityLevel desired_integrity_level_ = INTEGRITY_LEVEL_LAST; + MitigationFlags desired_mitigations_ = 0; + std::unique_ptr<Sid> app_container_sid_ = nullptr; + std::unique_ptr<Sid> lowbox_sid_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(PolicyDiagnostic); +}; + +} // namespace sandbox + +#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_ diff --git a/chromium/sandbox/win/src/sandbox_types.h b/chromium/sandbox/win/src/sandbox_types.h index e2638996185..2b97b50c201 100644 --- a/chromium/sandbox/win/src/sandbox_types.h +++ b/chromium/sandbox/win/src/sandbox_types.h @@ -113,6 +113,32 @@ enum ResultCode : int { SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_ACCESS_CHECK = 45, // Cannot create the AppContainer as adding a capability failed. SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY = 46, + // Cannot initialize a job object. + SBOX_ERROR_CANNOT_INIT_JOB = 47, + // Invalid LowBox SID string. + SBOX_ERROR_INVALID_LOWBOX_SID = 48, + // Cannot create restricted token. + SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN = 49, + // Cannot set the integrity level on a desktop object. + SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY = 50, + // Cannot create a LowBox token. + SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN = 51, + // Cannot modify LowBox token's DACL. + SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL = 52, + // Cannot create restricted impersonation token. + SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN = 53, + // Cannot duplicate target process handle. + SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE = 54, + // Cannot load executable for variable transfer. + SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE = 55, + // Cannot find variable address for transfer. + SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS = 56, + // Cannot write variable value. + SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE = 57, + // Short write to variable. + SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE = 58, + // Cannot initialize BrokerServices. + SBOX_ERROR_CANNOT_INIT_BROKERSERVICES = 59, // Placeholder for last item of the enum. SBOX_ERROR_LAST }; diff --git a/chromium/sandbox/win/src/target_interceptions.cc b/chromium/sandbox/win/src/target_interceptions.cc index 2dc2fd1456d..1b467814c6c 100644 --- a/chromium/sandbox/win/src/target_interceptions.cc +++ b/chromium/sandbox/win/src/target_interceptions.cc @@ -4,6 +4,7 @@ #include "sandbox/win/src/target_interceptions.h" +#include "base/win/static_constants.h" #include "sandbox/win/src/interception_agent.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/sandbox_nt_util.h" @@ -12,7 +13,6 @@ namespace sandbox { SANDBOX_INTERCEPT NtExports g_nt; -const char VERIFIER_DLL_NAME[] = "verifier.dll"; const char KERNEL32_DLL_NAME[] = "kernel32.dll"; enum SectionLoadState { @@ -60,8 +60,9 @@ TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection, // indicates Application Verifier is enabled and we should wait until // the next module is loaded. if (ansi_module_name && - (g_nt._strnicmp(ansi_module_name, VERIFIER_DLL_NAME, - sizeof(VERIFIER_DLL_NAME)) == 0)) + (g_nt._strnicmp( + ansi_module_name, base::win::kApplicationVerifierDllName, + g_nt.strlen(base::win::kApplicationVerifierDllName) + 1) == 0)) break; if (ansi_module_name && diff --git a/chromium/sandbox/win/src/target_process.cc b/chromium/sandbox/win/src/target_process.cc index 0d999187b16..ff831ada601 100644 --- a/chromium/sandbox/win/src/target_process.cc +++ b/chromium/sandbox/win/src/target_process.cc @@ -234,13 +234,13 @@ ResultCode TargetProcess::TransferVariable(const char* name, #if SANDBOX_EXPORTS HMODULE module = ::LoadLibrary(exe_name_.get()); if (!module) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE; child_var = ::GetProcAddress(module, name); ::FreeLibrary(module); if (!child_var) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS; size_t offset = reinterpret_cast<char*>(child_var) - reinterpret_cast<char*>(module); @@ -250,10 +250,10 @@ ResultCode TargetProcess::TransferVariable(const char* name, SIZE_T written; if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), child_var, address, size, &written)) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE; if (written != size) - return SBOX_ERROR_GENERIC; + return SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE; return SBOX_ALL_OK; } |