From 3f0f86b0caed75241fa71c95a5d73bc0164348c5 Mon Sep 17 00:00:00 2001 From: Andras Becsi Date: Tue, 18 Mar 2014 13:16:26 +0100 Subject: Update to new stable branch 1750 This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai Reviewed-by: Zeno Albisser --- chromium/sandbox/OWNERS | 2 + chromium/sandbox/linux/sandbox_linux.gypi | 67 +- .../sandbox/linux/sandbox_linux_test_sources.gypi | 6 + .../linux/seccomp-bpf-helpers/baseline_policy.cc | 175 ++++ .../linux/seccomp-bpf-helpers/baseline_policy.h | 43 + .../linux/seccomp-bpf-helpers/sigsys_handlers.cc | 146 +++ .../linux/seccomp-bpf-helpers/sigsys_handlers.h | 39 + .../syscall_parameters_restrictions.cc | 214 +++++ .../syscall_parameters_restrictions.h | 58 ++ .../linux/seccomp-bpf-helpers/syscall_sets.cc | 981 +++++++++++++++++++++ .../linux/seccomp-bpf-helpers/syscall_sets.h | 105 +++ chromium/sandbox/linux/seccomp-bpf/Makefile | 30 - chromium/sandbox/linux/seccomp-bpf/basicblock.cc | 11 +- chromium/sandbox/linux/seccomp-bpf/basicblock.h | 22 +- chromium/sandbox/linux/seccomp-bpf/bpf_tests.h | 72 +- chromium/sandbox/linux/seccomp-bpf/codegen.cc | 347 ++++---- chromium/sandbox/linux/seccomp-bpf/codegen.h | 71 +- .../sandbox/linux/seccomp-bpf/codegen_unittest.cc | 10 +- chromium/sandbox/linux/seccomp-bpf/demo.cc | 20 +- chromium/sandbox/linux/seccomp-bpf/die.cc | 35 +- chromium/sandbox/linux/seccomp-bpf/die.h | 37 +- chromium/sandbox/linux/seccomp-bpf/errorcode.cc | 50 +- chromium/sandbox/linux/seccomp-bpf/errorcode.h | 61 +- .../linux/seccomp-bpf/errorcode_unittest.cc | 14 +- chromium/sandbox/linux/seccomp-bpf/instruction.h | 17 +- chromium/sandbox/linux/seccomp-bpf/port.h | 36 - chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 789 +++++++++-------- chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h | 81 +- .../sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h | 35 + .../linux/seccomp-bpf/sandbox_bpf_policy_forward.h | 23 - .../linux/seccomp-bpf/sandbox_bpf_unittest.cc | 626 +++++++------ chromium/sandbox/linux/seccomp-bpf/syscall.cc | 16 +- chromium/sandbox/linux/seccomp-bpf/syscall.h | 101 ++- .../sandbox/linux/seccomp-bpf/syscall_iterator.cc | 35 +- .../sandbox/linux/seccomp-bpf/syscall_iterator.h | 15 +- .../linux/seccomp-bpf/syscall_iterator_unittest.cc | 7 +- .../sandbox/linux/seccomp-bpf/syscall_unittest.cc | 112 ++- chromium/sandbox/linux/seccomp-bpf/trap.cc | 103 +-- chromium/sandbox/linux/seccomp-bpf/trap.h | 47 +- chromium/sandbox/linux/seccomp-bpf/verifier.cc | 519 ++++++----- chromium/sandbox/linux/seccomp-bpf/verifier.h | 13 +- chromium/sandbox/linux/services/broker_process.cc | 33 +- chromium/sandbox/linux/services/broker_process.h | 43 +- .../linux/services/broker_process_unittest.cc | 134 +-- chromium/sandbox/linux/services/credentials.cc | 272 ++++++ chromium/sandbox/linux/services/credentials.h | 79 ++ .../sandbox/linux/services/credentials_unittest.cc | 215 +++++ .../sandbox/linux/services/init_process_reaper.cc | 101 +++ .../sandbox/linux/services/init_process_reaper.h | 23 + chromium/sandbox/linux/services/thread_helpers.cc | 84 ++ chromium/sandbox/linux/services/thread_helpers.h | 33 + .../linux/services/thread_helpers_unittests.cc | 93 ++ .../linux/suid/client/setuid_sandbox_client.cc | 6 + .../linux/suid/client/setuid_sandbox_client.h | 6 + chromium/sandbox/linux/suid/common/sandbox.h | 3 - chromium/sandbox/linux/suid/process_util_linux.c | 31 - chromium/sandbox/linux/suid/sandbox.c | 28 +- chromium/sandbox/win/sandbox_poc/main_ui_window.cc | 21 +- chromium/sandbox/win/sandbox_poc/main_ui_window.h | 13 +- chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc | 4 +- chromium/sandbox/win/sandbox_poc/sandbox.cc | 8 +- chromium/sandbox/win/sandbox_win.gypi | 2 +- chromium/sandbox/win/src/Wow64.cc | 11 +- chromium/sandbox/win/src/acl.cc | 20 +- chromium/sandbox/win/src/acl.h | 20 +- chromium/sandbox/win/src/crosscall_params.h | 3 +- chromium/sandbox/win/src/crosscall_server.cc | 2 +- chromium/sandbox/win/src/crosscall_server.h | 3 +- chromium/sandbox/win/src/file_policy_test.cc | 30 +- chromium/sandbox/win/src/filesystem_dispatcher.cc | 10 +- chromium/sandbox/win/src/filesystem_dispatcher.h | 13 +- chromium/sandbox/win/src/filesystem_policy.cc | 12 +- chromium/sandbox/win/src/filesystem_policy.h | 11 +- chromium/sandbox/win/src/handle_policy_test.cc | 10 +- chromium/sandbox/win/src/interception.cc | 9 +- chromium/sandbox/win/src/interception.h | 11 +- chromium/sandbox/win/src/interceptors_64.cc | 27 +- chromium/sandbox/win/src/interceptors_64.h | 17 +- chromium/sandbox/win/src/ipc_unittest.cc | 4 +- chromium/sandbox/win/src/job_unittest.cc | 5 +- chromium/sandbox/win/src/named_pipe_dispatcher.cc | 34 +- chromium/sandbox/win/src/named_pipe_dispatcher.h | 3 +- .../sandbox/win/src/named_pipe_interception.cc | 4 +- chromium/sandbox/win/src/named_pipe_policy.cc | 2 +- chromium/sandbox/win/src/named_pipe_policy.h | 3 +- chromium/sandbox/win/src/named_pipe_policy_test.cc | 75 +- chromium/sandbox/win/src/nt_internals.h | 33 +- chromium/sandbox/win/src/policy_broker.cc | 1 + chromium/sandbox/win/src/policy_low_level.cc | 4 +- chromium/sandbox/win/src/policy_low_level.h | 3 +- chromium/sandbox/win/src/policy_target_test.cc | 25 +- chromium/sandbox/win/src/process_policy_test.cc | 24 +- .../sandbox/win/src/process_thread_dispatcher.cc | 30 +- .../sandbox/win/src/process_thread_dispatcher.h | 8 +- .../sandbox/win/src/process_thread_interception.cc | 8 +- chromium/sandbox/win/src/process_thread_policy.cc | 4 +- chromium/sandbox/win/src/process_thread_policy.h | 5 +- chromium/sandbox/win/src/registry_dispatcher.cc | 12 +- chromium/sandbox/win/src/registry_dispatcher.h | 5 +- chromium/sandbox/win/src/registry_policy.cc | 6 +- chromium/sandbox/win/src/registry_policy.h | 5 +- chromium/sandbox/win/src/restricted_token.cc | 2 +- chromium/sandbox/win/src/restricted_token.h | 5 +- .../sandbox/win/src/restricted_token_unittest.cc | 2 +- chromium/sandbox/win/src/restricted_token_utils.cc | 9 +- chromium/sandbox/win/src/sandbox.cc | 2 +- chromium/sandbox/win/src/sandbox_globals.cc | 18 + chromium/sandbox/win/src/sandbox_nt_types.h | 1 + chromium/sandbox/win/src/sandbox_nt_util.cc | 12 +- chromium/sandbox/win/src/sandbox_nt_util.h | 3 +- chromium/sandbox/win/src/sandbox_policy.h | 3 +- chromium/sandbox/win/src/sandbox_utils.cc | 6 +- chromium/sandbox/win/src/sandbox_utils.h | 11 +- chromium/sandbox/win/src/service_resolver.cc | 12 +- chromium/sandbox/win/src/service_resolver.h | 3 + chromium/sandbox/win/src/service_resolver_64.cc | 16 +- .../sandbox/win/src/service_resolver_unittest.cc | 41 +- chromium/sandbox/win/src/sharedmem_ipc_server.cc | 4 +- chromium/sandbox/win/src/sync_dispatcher.cc | 33 +- chromium/sandbox/win/src/sync_dispatcher.h | 6 +- chromium/sandbox/win/src/sync_interception.cc | 156 ++-- chromium/sandbox/win/src/sync_interception.h | 47 +- chromium/sandbox/win/src/sync_policy.cc | 175 +++- chromium/sandbox/win/src/sync_policy.h | 8 +- chromium/sandbox/win/src/sync_policy_test.cc | 4 +- chromium/sandbox/win/src/target_process.cc | 6 +- chromium/sandbox/win/src/win_utils.cc | 47 +- chromium/sandbox/win/src/win_utils.h | 16 +- chromium/sandbox/win/src/win_utils_unittest.cc | 15 +- chromium/sandbox/win/src/window.cc | 33 +- chromium/sandbox/win/src/window.h | 5 +- chromium/sandbox/win/wow_helper/wow_helper.cc | 4 +- 132 files changed, 5451 insertions(+), 2189 deletions(-) create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc create mode 100644 chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h delete mode 100644 chromium/sandbox/linux/seccomp-bpf/Makefile delete mode 100644 chromium/sandbox/linux/seccomp-bpf/port.h create mode 100644 chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h delete mode 100644 chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h create mode 100644 chromium/sandbox/linux/services/credentials.cc create mode 100644 chromium/sandbox/linux/services/credentials.h create mode 100644 chromium/sandbox/linux/services/credentials_unittest.cc create mode 100644 chromium/sandbox/linux/services/init_process_reaper.cc create mode 100644 chromium/sandbox/linux/services/init_process_reaper.h create mode 100644 chromium/sandbox/linux/services/thread_helpers.cc create mode 100644 chromium/sandbox/linux/services/thread_helpers.h create mode 100644 chromium/sandbox/linux/services/thread_helpers_unittests.cc create mode 100644 chromium/sandbox/win/src/sandbox_globals.cc (limited to 'chromium/sandbox') diff --git a/chromium/sandbox/OWNERS b/chromium/sandbox/OWNERS index 018422fcc80..5d15856389d 100644 --- a/chromium/sandbox/OWNERS +++ b/chromium/sandbox/OWNERS @@ -1,3 +1,4 @@ +# For Windows: cpu@chromium.org jschuh@chromium.org nsylvain@chromium.org @@ -6,3 +7,4 @@ rvargas@chromium.org markus@chromium.org jln@chromium.org cevans@chromium.org +jorgelo@chromium.org diff --git a/chromium/sandbox/linux/sandbox_linux.gypi b/chromium/sandbox/linux/sandbox_linux.gypi index 29639f8f648..0e211f6c320 100644 --- a/chromium/sandbox/linux/sandbox_linux.gypi +++ b/chromium/sandbox/linux/sandbox_linux.gypi @@ -7,8 +7,10 @@ 'conditions': [ ['OS=="linux"', { 'compile_suid_client': 1, + 'compile_credentials': 1, }, { 'compile_suid_client': 0, + 'compile_credentials': 0, }], ['((OS=="linux" or OS=="android") and ' '(target_arch=="ia32" or target_arch=="x64" or ' @@ -17,6 +19,11 @@ }, { 'compile_seccomp_bpf': 0, }], + ['OS=="linux" and (target_arch=="ia32" or target_arch=="x64")', { + 'compile_seccomp_bpf_demo': 1, + }, { + 'compile_seccomp_bpf_demo': 0, + }], ], }, 'target_defaults': { @@ -51,6 +58,7 @@ [ 'compile_seccomp_bpf==1', { 'dependencies': [ 'seccomp_bpf', + 'seccomp_bpf_helpers', ], }], ], @@ -98,10 +106,9 @@ 'seccomp-bpf/errorcode.h', 'seccomp-bpf/instruction.h', 'seccomp-bpf/linux_seccomp.h', - 'seccomp-bpf/port.h', 'seccomp-bpf/sandbox_bpf.cc', 'seccomp-bpf/sandbox_bpf.h', - 'seccomp-bpf/sandbox_bpf_policy_forward.h', + 'seccomp-bpf/sandbox_bpf_policy.h', 'seccomp-bpf/syscall.cc', 'seccomp-bpf/syscall.h', 'seccomp-bpf/syscall_iterator.cc', @@ -119,6 +126,45 @@ '../..', ], }, + { + 'target_name': 'seccomp_bpf_helpers', + 'type': 'static_library', + 'sources': [ + 'seccomp-bpf-helpers/baseline_policy.cc', + 'seccomp-bpf-helpers/baseline_policy.h', + 'seccomp-bpf-helpers/sigsys_handlers.cc', + 'seccomp-bpf-helpers/sigsys_handlers.h', + 'seccomp-bpf-helpers/syscall_parameters_restrictions.cc', + 'seccomp-bpf-helpers/syscall_parameters_restrictions.h', + 'seccomp-bpf-helpers/syscall_sets.cc', + 'seccomp-bpf-helpers/syscall_sets.h', + ], + 'dependencies': [ + ], + 'include_dirs': [ + '../..', + ], + }, + { + # A demonstration program for the seccomp-bpf sandbox. + 'target_name': 'seccomp_bpf_demo', + 'conditions': [ + ['compile_seccomp_bpf_demo==1', { + 'type': 'executable', + 'sources': [ + 'seccomp-bpf/demo.cc', + ], + 'dependencies': [ + 'seccomp_bpf', + ], + }, { + 'type': 'none', + }], + ], + 'include_dirs': [ + '../../', + ], + }, { # The setuid sandbox, for Linux 'target_name': 'chrome_sandbox', @@ -145,10 +191,26 @@ 'sources': [ 'services/broker_process.cc', 'services/broker_process.h', + 'services/init_process_reaper.cc', + 'services/init_process_reaper.h', + 'services/thread_helpers.cc', + 'services/thread_helpers.h', ], 'dependencies': [ '../base/base.gyp:base', ], + 'conditions': [ + ['compile_credentials==1', { + 'sources': [ + 'services/credentials.cc', + 'services/credentials.h', + ], + 'dependencies': [ + # for capabilities.cc. + '../build/linux/system.gyp:libcap', + ], + }], + ], 'include_dirs': [ '..', ], @@ -195,6 +257,7 @@ ], 'dependencies': [ '../base/base.gyp:base', + 'sandbox_services', ], 'include_dirs': [ '..', diff --git a/chromium/sandbox/linux/sandbox_linux_test_sources.gypi b/chromium/sandbox/linux/sandbox_linux_test_sources.gypi index 81190cd564e..a6a916fee45 100644 --- a/chromium/sandbox/linux/sandbox_linux_test_sources.gypi +++ b/chromium/sandbox/linux/sandbox_linux_test_sources.gypi @@ -18,6 +18,7 @@ 'tests/unit_tests.cc', 'tests/unit_tests.h', 'services/broker_process_unittest.cc', + 'services/thread_helpers_unittests.cc', ], 'conditions': [ [ 'compile_suid_client==1', { @@ -35,5 +36,10 @@ 'seccomp-bpf/syscall_unittest.cc', ], }], + [ 'compile_credentials==1', { + 'sources': [ + 'services/credentials_unittest.cc', + ], + }], ], } diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc new file mode 100644 index 00000000000..d0e53e39bc8 --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2013 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/linux/seccomp-bpf-helpers/baseline_policy.h" + +#include +#include +#include +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" +#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" +#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" +#include "sandbox/linux/services/linux_syscalls.h" + +// Changing this implementation will have an effect on *all* policies. +// Currently this means: Renderer/Worker, GPU, Flash and NaCl. + +namespace sandbox { + +namespace { + +bool IsBaselinePolicyAllowed(int sysno) { + return SyscallSets::IsAllowedAddressSpaceAccess(sysno) || + SyscallSets::IsAllowedBasicScheduler(sysno) || + SyscallSets::IsAllowedEpoll(sysno) || + SyscallSets::IsAllowedFileSystemAccessViaFd(sysno) || + SyscallSets::IsAllowedGeneralIo(sysno) || + SyscallSets::IsAllowedGetOrModifySocket(sysno) || + SyscallSets::IsAllowedGettime(sysno) || + SyscallSets::IsAllowedPrctl(sysno) || + SyscallSets::IsAllowedProcessStartOrDeath(sysno) || + SyscallSets::IsAllowedSignalHandling(sysno) || + SyscallSets::IsFutex(sysno) || + SyscallSets::IsGetSimpleId(sysno) || + SyscallSets::IsKernelInternalApi(sysno) || +#if defined(__arm__) + SyscallSets::IsArmPrivate(sysno) || +#endif + SyscallSets::IsKill(sysno) || + SyscallSets::IsAllowedOperationOnFd(sysno); +} + +// System calls that will trigger the crashing SIGSYS handler. +bool IsBaselinePolicyWatched(int sysno) { + return SyscallSets::IsAdminOperation(sysno) || + SyscallSets::IsAdvancedScheduler(sysno) || + SyscallSets::IsAdvancedTimer(sysno) || + SyscallSets::IsAsyncIo(sysno) || + SyscallSets::IsDebug(sysno) || + SyscallSets::IsEventFd(sysno) || + SyscallSets::IsExtendedAttributes(sysno) || + SyscallSets::IsFaNotify(sysno) || + SyscallSets::IsFsControl(sysno) || + SyscallSets::IsGlobalFSViewChange(sysno) || + SyscallSets::IsGlobalProcessEnvironment(sysno) || + SyscallSets::IsGlobalSystemStatus(sysno) || + SyscallSets::IsInotify(sysno) || + SyscallSets::IsKernelModule(sysno) || + SyscallSets::IsKeyManagement(sysno) || + SyscallSets::IsMessageQueue(sysno) || + SyscallSets::IsMisc(sysno) || +#if defined(__x86_64__) + SyscallSets::IsNetworkSocketInformation(sysno) || +#endif + SyscallSets::IsNuma(sysno) || + SyscallSets::IsProcessGroupOrSession(sysno) || + SyscallSets::IsProcessPrivilegeChange(sysno) || +#if defined(__i386__) + SyscallSets::IsSocketCall(sysno) || +#endif +#if defined(__arm__) + SyscallSets::IsArmPciConfig(sysno) || +#endif + SyscallSets::IsTimer(sysno); +} + +// |fs_denied_errno| is the errno return for denied filesystem access. +ErrorCode EvaluateSyscallImpl(int fs_denied_errno, SandboxBPF* sandbox, + int sysno) { + if (IsBaselinePolicyAllowed(sysno)) { + return ErrorCode(ErrorCode::ERR_ALLOWED); + } + +#if defined(__x86_64__) || defined(__arm__) + if (sysno == __NR_socketpair) { + // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen. + COMPILE_ASSERT(AF_UNIX == PF_UNIX, af_unix_pf_unix_different); + return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, AF_UNIX, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Trap(CrashSIGSYS_Handler, NULL)); + } +#endif + + if (sysno == __NR_madvise) { + // Only allow MADV_DONTNEED (aka MADV_FREE). + return sandbox->Cond(2, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, MADV_DONTNEED, + ErrorCode(ErrorCode::ERR_ALLOWED), + ErrorCode(EPERM)); + } + +#if defined(__i386__) || defined(__x86_64__) + if (sysno == __NR_mmap) + return RestrictMmapFlags(sandbox); +#endif + +#if defined(__i386__) || defined(__arm__) + if (sysno == __NR_mmap2) + return RestrictMmapFlags(sandbox); +#endif + + if (sysno == __NR_mprotect) + return RestrictMprotectFlags(sandbox); + + if (sysno == __NR_fcntl) + return RestrictFcntlCommands(sandbox); + +#if defined(__i386__) || defined(__arm__) + if (sysno == __NR_fcntl64) + return RestrictFcntlCommands(sandbox); +#endif + + if (SyscallSets::IsFileSystem(sysno) || + SyscallSets::IsCurrentDirectory(sysno)) { + return ErrorCode(fs_denied_errno); + } + + if (SyscallSets::IsAnySystemV(sysno)) { + return ErrorCode(EPERM); + } + + if (SyscallSets::IsUmask(sysno) || + SyscallSets::IsDeniedFileSystemAccessViaFd(sysno) || + SyscallSets::IsDeniedGetOrModifySocket(sysno)) { + return ErrorCode(EPERM); + } + +#if defined(__i386__) + if (SyscallSets::IsSocketCall(sysno)) + return RestrictSocketcallCommand(sandbox); +#endif + + if (IsBaselinePolicyWatched(sysno)) { + // Previously unseen syscalls. TODO(jln): some of these should + // be denied gracefully right away. + return sandbox->Trap(CrashSIGSYS_Handler, NULL); + } + // In any other case crash the program with our SIGSYS handler. + return sandbox->Trap(CrashSIGSYS_Handler, NULL); +} + +} // namespace. + +// Unfortunately C++03 doesn't allow delegated constructors. +// Call other constructor when C++11 lands. +BaselinePolicy::BaselinePolicy() + : fs_denied_errno_(EPERM) {} + +BaselinePolicy::BaselinePolicy(int fs_denied_errno) + : fs_denied_errno_(fs_denied_errno) {} + +BaselinePolicy::~BaselinePolicy() {} + +ErrorCode BaselinePolicy::EvaluateSyscall(SandboxBPF* sandbox, + int sysno) const { + return EvaluateSyscallImpl(fs_denied_errno_, sandbox, sysno); +} + +} // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h new file mode 100644 index 00000000000..1dfd137fa3d --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h @@ -0,0 +1,43 @@ +// Copyright (c) 2013 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_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_ + +#include "sandbox/linux/seccomp-bpf/errorcode.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" + +namespace sandbox { + +class SandboxBPF; +class SandboxBPFPolicy; + +// This is a helper to build seccomp-bpf policies, i.e. policies for a sandbox +// that reduces the Linux kernel's attack surface. Given its nature, it doesn't +// have a clear semantics and is mostly "implementation-defined". +// +// This returns an object that implements the SandboxBPFPolicy interface with +// a "baseline" policy within Chromium. +// The "baseline" policy is somewhat arbitrary. All Chromium policies are an +// alteration of it, and it represents a reasonable common ground to run most +// code in a sandboxed environment. +class BaselinePolicy : public SandboxBPFPolicy { + public: + BaselinePolicy(); + // |fs_denied_errno| is the errno returned when a filesystem access system + // call is denied. + explicit BaselinePolicy(int fs_denied_errno); + virtual ~BaselinePolicy(); + + virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, + int system_call_number) const OVERRIDE; + + private: + int fs_denied_errno_; + DISALLOW_COPY_AND_ASSIGN(BaselinePolicy); +}; + +} // namespace sandbox. + +#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc new file mode 100644 index 00000000000..6ff71257526 --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2013 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. + +// Note: any code in this file MUST be async-signal safe. + +#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" + +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" + +namespace { + +inline bool IsArchitectureX86_64() { +#if defined(__x86_64__) + return true; +#else + return false; +#endif +} + +// Write |error_message| to stderr. Similar to RawLog(), but a bit more careful +// about async-signal safety. |size| is the size to write and should typically +// not include a terminating \0. +void WriteToStdErr(const char* error_message, size_t size) { + while (size > 0) { + // TODO(jln): query the current policy to check if send() is available and + // use it to perform a non-blocking write. + const int ret = HANDLE_EINTR(write(STDERR_FILENO, error_message, size)); + // We can't handle any type of error here. + if (ret <= 0 || static_cast(ret) > size) break; + size -= ret; + error_message += ret; + } +} + +// Print a seccomp-bpf failure to handle |sysno| to stderr in an +// async-signal safe way. +void PrintSyscallError(uint32_t sysno) { + if (sysno >= 1024) + sysno = 0; + // TODO(markus): replace with async-signal safe snprintf when available. + const size_t kNumDigits = 4; + char sysno_base10[kNumDigits]; + uint32_t rem = sysno; + uint32_t mod = 0; + for (int i = kNumDigits - 1; i >= 0; i--) { + mod = rem % 10; + rem /= 10; + sysno_base10[i] = '0' + mod; + } + static const char kSeccompErrorPrefix[] = + __FILE__":**CRASHING**:seccomp-bpf failure in syscall "; + static const char kSeccompErrorPostfix[] = "\n"; + WriteToStdErr(kSeccompErrorPrefix, sizeof(kSeccompErrorPrefix) - 1); + WriteToStdErr(sysno_base10, sizeof(sysno_base10)); + WriteToStdErr(kSeccompErrorPostfix, sizeof(kSeccompErrorPostfix) - 1); +} + +} // namespace. + +namespace sandbox { + +intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) { + uint32_t syscall = args.nr; + if (syscall >= 1024) + syscall = 0; + PrintSyscallError(syscall); + + // Encode 8-bits of the 1st two arguments too, so we can discern which socket + // type, which fcntl, ... etc., without being likely to hit a mapped + // address. + // Do not encode more bits here without thinking about increasing the + // likelihood of collision with mapped pages. + syscall |= ((args.args[0] & 0xffUL) << 12); + syscall |= ((args.args[1] & 0xffUL) << 20); + // Purposefully dereference the syscall as an address so it'll show up very + // clearly and easily in crash dumps. + volatile char* addr = reinterpret_cast(syscall); + *addr = '\0'; + // In case we hit a mapped address, hit the null page with just the syscall, + // for paranoia. + syscall &= 0xfffUL; + addr = reinterpret_cast(syscall); + *addr = '\0'; + for (;;) + _exit(1); +} + +// TODO(jln): refactor the reporting functions. + +intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) { + // "flags" is the first argument in the kernel's clone(). + // Mark as volatile to be able to find the value on the stack in a minidump. +#if !defined(NDEBUG) + RAW_LOG(ERROR, __FILE__":**CRASHING**:clone() failure\n"); +#endif + volatile uint64_t clone_flags = args.args[0]; + volatile char* addr; + if (IsArchitectureX86_64()) { + addr = reinterpret_cast(clone_flags & 0xFFFFFF); + *addr = '\0'; + } + // Hit the NULL page if this fails to fault. + addr = reinterpret_cast(clone_flags & 0xFFF); + *addr = '\0'; + for (;;) + _exit(1); +} + +intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args, + void* /* aux */) { + // Mark as volatile to be able to find the value on the stack in a minidump. +#if !defined(NDEBUG) + RAW_LOG(ERROR, __FILE__":**CRASHING**:prctl() failure\n"); +#endif + volatile uint64_t option = args.args[0]; + volatile char* addr = + reinterpret_cast(option & 0xFFF); + *addr = '\0'; + for (;;) + _exit(1); +} + +intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args, + void* /* aux */) { + // Make "request" volatile so that we can see it on the stack in a minidump. +#if !defined(NDEBUG) + RAW_LOG(ERROR, __FILE__":**CRASHING**:ioctl() failure\n"); +#endif + volatile uint64_t request = args.args[1]; + volatile char* addr = reinterpret_cast(request & 0xFFFF); + *addr = '\0'; + // Hit the NULL page if this fails. + addr = reinterpret_cast(request & 0xFFF); + *addr = '\0'; + for (;;) + _exit(1); +} + +} // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h new file mode 100644 index 00000000000..3bf5c16db1f --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h @@ -0,0 +1,39 @@ +// Copyright (c) 2013 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_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_ + +#include "base/basictypes.h" +#include "build/build_config.h" + +// The handlers are suitable for use in Trap() error codes. They are +// guaranteed to be async-signal safe. +// See sandbox/linux/seccomp-bpf/trap.h to see how they work. + +namespace sandbox { + +struct arch_seccomp_data; + +// This handler will crash the currently running process. The crashing address +// will be the number of the current system call, extracted from |args|. +// This handler will also print to stderr the number of the crashing syscall. +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 crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is +// the clone(2) argument, extracted from |args|. +intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux); +// The crashing address will be (option & 0xFFF), where option is the prctl(2) +// argument. +intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args, void* aux); +// The crashing address will be request & 0xFFFF, where request is the ioctl(2) +// argument. +intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args, void* aux); + +} // namespace sandbox. + +#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc new file mode 100644 index 00000000000..9b417ce221f --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc @@ -0,0 +1,214 @@ +// Copyright (c) 2013 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/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" +#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" + +#if defined(OS_ANDROID) +#if !defined(F_DUPFD_CLOEXEC) +#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) +#endif +#endif + +#if defined(__arm__) && !defined(MAP_STACK) +#define MAP_STACK 0x20000 // Daisy build environment has old headers. +#endif + +namespace { + +inline bool RunningOnASAN() { +#if defined(ADDRESS_SANITIZER) + return true; +#else + return false; +#endif +} + +inline bool IsArchitectureX86_64() { +#if defined(__x86_64__) + return true; +#else + return false; +#endif +} + +inline bool IsArchitectureI386() { +#if defined(__i386__) + return true; +#else + return false; +#endif +} + +} // namespace. + +namespace sandbox { + +ErrorCode RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox) { + // Glibc's pthread. + if (!RunningOnASAN()) { + return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | + CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + CLONE_PARENT_SETTID | SIGCHLD, + ErrorCode(EPERM), + // ARM + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, + ErrorCode(EPERM), + sandbox->Trap(SIGSYSCloneFailure, NULL)))); + } else { + return ErrorCode(ErrorCode::ERR_ALLOWED); + } +} + +ErrorCode RestrictPrctl(SandboxBPF* sandbox) { + // Will need to add seccomp compositing in the future. PR_SET_PTRACER is + // used by breakpad but not needed anymore. + return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + PR_SET_NAME, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + PR_SET_DUMPABLE, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + PR_GET_DUMPABLE, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Trap(SIGSYSPrctlFailure, NULL)))); +} + +ErrorCode RestrictIoctl(SandboxBPF* sandbox) { + return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, TCGETS, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, FIONREAD, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Trap(SIGSYSIoctlFailure, NULL))); +} + +ErrorCode RestrictMmapFlags(SandboxBPF* sandbox) { + // The flags you see are actually the allowed ones, and the variable is a + // "denied" mask because of the negation operator. + // Significantly, we don't permit MAP_HUGETLB, or the newer flags such as + // MAP_POPULATE. + // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries. + uint32_t denied_mask = ~(MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS | + MAP_STACK | MAP_NORESERVE | MAP_FIXED | + MAP_DENYWRITE); + return sandbox->Cond(3, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, + denied_mask, + sandbox->Trap(CrashSIGSYS_Handler, NULL), + ErrorCode(ErrorCode::ERR_ALLOWED)); +} + +ErrorCode RestrictMprotectFlags(SandboxBPF* sandbox) { + // The flags you see are actually the allowed ones, and the variable is a + // "denied" mask because of the negation operator. + // Significantly, we don't permit weird undocumented flags such as + // PROT_GROWSDOWN. + uint32_t denied_mask = ~(PROT_READ | PROT_WRITE | PROT_EXEC); + return sandbox->Cond(2, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, + denied_mask, + sandbox->Trap(CrashSIGSYS_Handler, NULL), + ErrorCode(ErrorCode::ERR_ALLOWED)); +} + +ErrorCode RestrictFcntlCommands(SandboxBPF* sandbox) { + // We also restrict the flags in F_SETFL. We don't want to permit flags with + // a history of trouble such as O_DIRECT. The flags you see are actually the + // allowed ones, and the variable is a "denied" mask because of the negation + // operator. + // Glibc overrides the kernel's O_LARGEFILE value. Account for this. + int kOLargeFileFlag = O_LARGEFILE; + if (IsArchitectureX86_64() || IsArchitectureI386()) + kOLargeFileFlag = 0100000; + + // TODO(jln): add TP_LONG/TP_SIZET types. + ErrorCode::ArgType mask_long_type; + if (sizeof(long) == 8) + mask_long_type = ErrorCode::TP_64BIT; + else if (sizeof(long) == 4) + mask_long_type = ErrorCode::TP_32BIT; + else + NOTREACHED(); + + unsigned long denied_mask = ~(O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC | + kOLargeFileFlag | O_CLOEXEC | O_NOATIME); + return sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_GETFL, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_SETFL, + sandbox->Cond(2, mask_long_type, + ErrorCode::OP_HAS_ANY_BITS, denied_mask, + sandbox->Trap(CrashSIGSYS_Handler, NULL), + ErrorCode(ErrorCode::ERR_ALLOWED)), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_GETFD, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_SETFD, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_DUPFD, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_SETLK, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_SETLKW, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_GETLK, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(1, ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, F_DUPFD_CLOEXEC, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Trap(CrashSIGSYS_Handler, NULL)))))))))); +} + +#if defined(__i386__) +ErrorCode RestrictSocketcallCommand(SandboxBPF* sandbox) { + // Unfortunately, we are unable to restrict the first parameter to + // socketpair(2). Whilst initially sounding bad, it's noteworthy that very + // few protocols actually support socketpair(2). The scary call that we're + // worried about, socket(2), remains blocked. + return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_SOCKETPAIR, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_SEND, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_RECV, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_SENDTO, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_RECVFROM, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_SHUTDOWN, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_SENDMSG, ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + SYS_RECVMSG, ErrorCode(ErrorCode::ERR_ALLOWED), + ErrorCode(EPERM))))))))); +} +#endif + +} // 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 new file mode 100644 index 00000000000..65b7c472198 --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h @@ -0,0 +1,58 @@ +// Copyright (c) 2013 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_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_ + +#include "build/build_config.h" + +// These are helpers to build seccomp-bpf policies, i.e. policies for a +// sandbox that reduces the Linux kernel's attack surface. They return an +// ErrorCode suitable to restrict certain system call parameters. + +namespace sandbox { + +class ErrorCode; +class SandboxBPF; + +// Allow clone(2) for threads. +// Reject fork(2) attempts with EPERM. +// Don't restrict on ASAN. +// Crash if anything else is attempted. +ErrorCode RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox); + +// Allow PR_SET_NAME, PR_SET_DUMPABLE, PR_GET_DUMPABLE. +// Crash if anything else is attempted. +ErrorCode RestrictPrctl(SandboxBPF* sandbox); + +// Allow TCGETS and FIONREAD. +// Crash if anything else is attempted. +ErrorCode RestrictIoctl(SandboxBPF* sandbox); + +// Restrict the flags argument in mmap(2). +// Only allow: MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS | +// MAP_STACK | MAP_NORESERVE | MAP_FIXED | MAP_DENYWRITE. +// Crash if any other flag is used. +ErrorCode RestrictMmapFlags(SandboxBPF* sandbox); + +// Restrict the prot argument in mprotect(2). +// Only allow: PROT_READ | PROT_WRITE | PROT_EXEC. +ErrorCode RestrictMprotectFlags(SandboxBPF* sandbox); + +// Restrict fcntl(2) cmd argument to: +// We allow F_GETFL, F_SETFL, F_GETFD, F_SETFD, F_DUPFD, F_DUPFD_CLOEXEC, +// F_SETLK, F_SETLKW and F_GETLK. +// Also, in F_SETFL, restrict the allowed flags to: O_ACCMODE | O_APPEND | +// O_NONBLOCK | O_SYNC | O_LARGEFILE | O_CLOEXEC | O_NOATIME. +ErrorCode RestrictFcntlCommands(SandboxBPF* sandbox); + +#if defined(__i386__) +// Restrict socketcall(2) to only allow socketpair(2), send(2), recv(2), +// sendto(2), recvfrom(2), shutdown(2), sendmsg(2) and recvmsg(2). +ErrorCode RestrictSocketcallCommand(SandboxBPF* sandbox); +#endif + +} // namespace sandbox. + +#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc new file mode 100644 index 00000000000..032f6c3c472 --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc @@ -0,0 +1,981 @@ +// Copyright (c) 2013 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/linux/seccomp-bpf-helpers/syscall_sets.h" + +#include "build/build_config.h" +#include "sandbox/linux/services/linux_syscalls.h" + +namespace sandbox { + +// The functions below cover all existing i386, x86_64, and ARM system calls; +// excluding syscalls made obsolete in ARM EABI. +// The implicitly defined sets form a partition of the sets of +// system calls. + +// TODO(jln) we need to restrict the first parameter! +bool SyscallSets::IsKill(int sysno) { + switch (sysno) { + case __NR_kill: + case __NR_tkill: + case __NR_tgkill: + return true; + default: + return false; + } +} + +bool SyscallSets::IsAllowedGettime(int sysno) { + switch (sysno) { + case __NR_clock_gettime: + case __NR_gettimeofday: +#if defined(__i386__) || defined(__x86_64__) + case __NR_time: +#endif + return true; + case __NR_adjtimex: // Privileged. + case __NR_clock_adjtime: // Privileged. + case __NR_clock_getres: // Could be allowed. + case __NR_clock_nanosleep: // Could be allowed. + case __NR_clock_settime: // Privileged. +#if defined(__i386__) + case __NR_ftime: // Obsolete. +#endif + case __NR_settimeofday: // Privileged. +#if defined(__i386__) + case __NR_stime: +#endif + default: + return false; + } +} + +bool SyscallSets::IsCurrentDirectory(int sysno) { + switch (sysno) { + case __NR_getcwd: + case __NR_chdir: + case __NR_fchdir: + return true; + default: + return false; + } +} + +bool SyscallSets::IsUmask(int sysno) { + switch (sysno) { + case __NR_umask: + return true; + default: + return false; + } +} + +// System calls that directly access the file system. They might acquire +// a new file descriptor or otherwise perform an operation directly +// via a path. +// Both EPERM and ENOENT are valid errno unless otherwise noted in comment. +bool SyscallSets::IsFileSystem(int sysno) { + switch (sysno) { + case __NR_access: // EPERM not a valid errno. + case __NR_chmod: + case __NR_chown: +#if defined(__i386__) || defined(__arm__) + case __NR_chown32: +#endif + case __NR_creat: + case __NR_execve: + case __NR_faccessat: // EPERM not a valid errno. + case __NR_fchmodat: + case __NR_fchownat: // Should be called chownat ? +#if defined(__x86_64__) + case __NR_newfstatat: // fstatat(). EPERM not a valid errno. +#elif defined(__i386__) || defined(__arm__) + case __NR_fstatat64: +#endif + case __NR_futimesat: // Should be called utimesat ? + case __NR_lchown: +#if defined(__i386__) || defined(__arm__) + case __NR_lchown32: +#endif + case __NR_link: + case __NR_linkat: + case __NR_lookup_dcookie: // ENOENT not a valid errno. + case __NR_lstat: // EPERM not a valid errno. +#if defined(__i386__) + case __NR_oldlstat: +#endif +#if defined(__i386__) || defined(__arm__) + case __NR_lstat64: +#endif + case __NR_mkdir: + case __NR_mkdirat: + case __NR_mknod: + case __NR_mknodat: + case __NR_open: + case __NR_openat: + case __NR_readlink: // EPERM not a valid errno. + case __NR_readlinkat: + case __NR_rename: + case __NR_renameat: + case __NR_rmdir: + case __NR_stat: // EPERM not a valid errno. +#if defined(__i386__) + case __NR_oldstat: +#endif +#if defined(__i386__) || defined(__arm__) + case __NR_stat64: +#endif + case __NR_statfs: // EPERM not a valid errno. +#if defined(__i386__) || defined(__arm__) + case __NR_statfs64: +#endif + case __NR_symlink: + case __NR_symlinkat: + case __NR_truncate: +#if defined(__i386__) || defined(__arm__) + case __NR_truncate64: +#endif + case __NR_unlink: + case __NR_unlinkat: + case __NR_uselib: // Neither EPERM, nor ENOENT are valid errno. + case __NR_ustat: // Same as above. Deprecated. +#if defined(__i386__) || defined(__x86_64__) + case __NR_utime: +#endif + case __NR_utimensat: // New. + case __NR_utimes: + return true; + default: + return false; + } +} + +bool SyscallSets::IsAllowedFileSystemAccessViaFd(int sysno) { + switch (sysno) { + case __NR_fstat: +#if defined(__i386__) || defined(__arm__) + case __NR_fstat64: +#endif + return true; +// TODO(jln): these should be denied gracefully as well (moved below). +#if defined(__i386__) || defined(__x86_64__) + case __NR_fadvise64: // EPERM not a valid errno. +#endif +#if defined(__i386__) + case __NR_fadvise64_64: +#endif +#if defined(__arm__) + case __NR_arm_fadvise64_64: +#endif + case __NR_fdatasync: // EPERM not a valid errno. + case __NR_flock: // EPERM not a valid errno. + case __NR_fstatfs: // Give information about the whole filesystem. +#if defined(__i386__) || defined(__arm__) + case __NR_fstatfs64: +#endif + case __NR_fsync: // EPERM not a valid errno. +#if defined(__i386__) + case __NR_oldfstat: +#endif +#if defined(__i386__) || defined(__x86_64__) + case __NR_sync_file_range: // EPERM not a valid errno. +#elif defined(__arm__) + case __NR_arm_sync_file_range: // EPERM not a valid errno. +#endif + default: + return false; + } +} + +// EPERM is a good errno for any of these. +bool SyscallSets::IsDeniedFileSystemAccessViaFd(int sysno) { + switch (sysno) { + case __NR_fallocate: + case __NR_fchmod: + case __NR_fchown: + case __NR_ftruncate: +#if defined(__i386__) || defined(__arm__) + case __NR_fchown32: + case __NR_ftruncate64: +#endif + case __NR_getdents: // EPERM not a valid errno. + case __NR_getdents64: // EPERM not a valid errno. +#if defined(__i386__) + case __NR_readdir: +#endif + return true; + default: + return false; + } +} + +bool SyscallSets::IsGetSimpleId(int sysno) { + switch (sysno) { + case __NR_capget: + case __NR_getegid: + case __NR_geteuid: + case __NR_getgid: + case __NR_getgroups: + case __NR_getpid: + case __NR_getppid: + case __NR_getresgid: + case __NR_getsid: + case __NR_gettid: + case __NR_getuid: + case __NR_getresuid: +#if defined(__i386__) || defined(__arm__) + case __NR_getegid32: + case __NR_geteuid32: + case __NR_getgid32: + case __NR_getgroups32: + case __NR_getresgid32: + case __NR_getresuid32: + case __NR_getuid32: +#endif + return true; + default: + return false; + } +} + +bool SyscallSets::IsProcessPrivilegeChange(int sysno) { + switch (sysno) { + case __NR_capset: +#if defined(__i386__) || defined(__x86_64__) + case __NR_ioperm: // Intel privilege. + case __NR_iopl: // Intel privilege. +#endif + case __NR_setfsgid: + case __NR_setfsuid: + case __NR_setgid: + case __NR_setgroups: + case __NR_setregid: + case __NR_setresgid: + case __NR_setresuid: + case __NR_setreuid: + case __NR_setuid: +#if defined(__i386__) || defined(__arm__) + case __NR_setfsgid32: + case __NR_setfsuid32: + case __NR_setgid32: + case __NR_setgroups32: + case __NR_setregid32: + case __NR_setresgid32: + case __NR_setresuid32: + case __NR_setreuid32: + case __NR_setuid32: +#endif + return true; + default: + return false; + } +} + +bool SyscallSets::IsProcessGroupOrSession(int sysno) { + switch (sysno) { + case __NR_setpgid: + case __NR_getpgrp: + case __NR_setsid: + case __NR_getpgid: + return true; + default: + return false; + } +} + +bool SyscallSets::IsAllowedSignalHandling(int sysno) { + switch (sysno) { + case __NR_rt_sigaction: + case __NR_rt_sigprocmask: + case __NR_rt_sigreturn: +#if defined(__i386__) || defined(__arm__) + case __NR_sigaction: + case __NR_sigprocmask: + case __NR_sigreturn: +#endif + return true; + case __NR_rt_sigpending: + case __NR_rt_sigqueueinfo: + case __NR_rt_sigsuspend: + case __NR_rt_sigtimedwait: + case __NR_rt_tgsigqueueinfo: + case __NR_sigaltstack: + case __NR_signalfd: + case __NR_signalfd4: +#if defined(__i386__) || defined(__arm__) + case __NR_sigpending: + case __NR_sigsuspend: +#endif +#if defined(__i386__) + case __NR_signal: + case __NR_sgetmask: // Obsolete. + case __NR_ssetmask: +#endif + default: + return false; + } +} + +bool SyscallSets::IsAllowedOperationOnFd(int sysno) { + switch (sysno) { + case __NR_close: + case __NR_dup: + case __NR_dup2: + case __NR_dup3: +#if defined(__x86_64__) || defined(__arm__) + case __NR_shutdown: +#endif + return true; + case __NR_fcntl: +#if defined(__i386__) || defined(__arm__) + case __NR_fcntl64: +#endif + default: + return false; + } +} + +bool SyscallSets::IsKernelInternalApi(int sysno) { + switch (sysno) { + case __NR_restart_syscall: +#if defined(__arm__) + case __ARM_NR_cmpxchg: +#endif + return true; + default: + return false; + } +} + +// This should be thought through in conjunction with IsFutex(). +bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) { + switch (sysno) { + case __NR_clone: // TODO(jln): restrict flags. + case __NR_exit: + case __NR_exit_group: + case __NR_wait4: + case __NR_waitid: +#if defined(__i386__) + case __NR_waitpid: +#endif + return true; + case __NR_setns: // Privileged. + case __NR_fork: +#if defined(__i386__) || defined(__x86_64__) + case __NR_get_thread_area: + case __NR_set_thread_area: +#endif + case __NR_set_tid_address: + case __NR_unshare: + case __NR_vfork: + default: + return false; + } +} + +// It's difficult to restrict those, but there is attack surface here. +bool SyscallSets::IsFutex(int sysno) { + switch (sysno) { + case __NR_futex: + case __NR_get_robust_list: + case __NR_set_robust_list: + return true; + default: + return false; + } +} + +bool SyscallSets::IsAllowedEpoll(int sysno) { + switch (sysno) { + case __NR_epoll_create: + case __NR_epoll_create1: + case __NR_epoll_ctl: + case __NR_epoll_wait: + return true; + default: +#if defined(__x86_64__) + case __NR_epoll_ctl_old: +#endif + case __NR_epoll_pwait: +#if defined(__x86_64__) + case __NR_epoll_wait_old: +#endif + return false; + } +} + +bool SyscallSets::IsAllowedGetOrModifySocket(int sysno) { + switch (sysno) { + case __NR_pipe: + case __NR_pipe2: + return true; + default: +#if defined(__x86_64__) || defined(__arm__) + case __NR_socketpair: // We will want to inspect its argument. +#endif + return false; + } +} + +bool SyscallSets::IsDeniedGetOrModifySocket(int sysno) { + switch (sysno) { +#if defined(__x86_64__) || defined(__arm__) + case __NR_accept: + case __NR_accept4: + case __NR_bind: + case __NR_connect: + case __NR_socket: + case __NR_listen: + return true; +#endif + default: + return false; + } +} + +#if defined(__i386__) +// Big multiplexing system call for sockets. +bool SyscallSets::IsSocketCall(int sysno) { + switch (sysno) { + case __NR_socketcall: + return true; + default: + return false; + } +} +#endif + +#if defined(__x86_64__) || defined(__arm__) +bool SyscallSets::IsNetworkSocketInformation(int sysno) { + switch (sysno) { + case __NR_getpeername: + case __NR_getsockname: + case __NR_getsockopt: + case __NR_setsockopt: + return true; + default: + return false; + } +} +#endif + +bool SyscallSets::IsAllowedAddressSpaceAccess(int sysno) { + switch (sysno) { + case __NR_brk: + case __NR_mlock: + case __NR_munlock: + case __NR_munmap: + return true; + case __NR_madvise: + case __NR_mincore: + case __NR_mlockall: +#if defined(__i386__) || defined(__x86_64__) + case __NR_mmap: +#endif +#if defined(__i386__) || defined(__arm__) + case __NR_mmap2: +#endif +#if defined(__i386__) || defined(__x86_64__) + case __NR_modify_ldt: +#endif + case __NR_mprotect: + case __NR_mremap: + case __NR_msync: + case __NR_munlockall: + case __NR_readahead: + case __NR_remap_file_pages: +#if defined(__i386__) + case __NR_vm86: + case __NR_vm86old: +#endif + default: + return false; + } +} + +bool SyscallSets::IsAllowedGeneralIo(int sysno) { + switch (sysno) { + case __NR_lseek: +#if defined(__i386__) || defined(__arm__) + case __NR__llseek: +#endif + case __NR_poll: + case __NR_ppoll: + case __NR_pselect6: + case __NR_read: + case __NR_readv: +#if defined(__arm__) + case __NR_recv: +#endif +#if defined(__x86_64__) || defined(__arm__) + case __NR_recvfrom: // Could specify source. + case __NR_recvmsg: // Could specify source. +#endif +#if defined(__i386__) || defined(__x86_64__) + case __NR_select: +#endif +#if defined(__i386__) || defined(__arm__) + case __NR__newselect: +#endif +#if defined(__arm__) + case __NR_send: +#endif +#if defined(__x86_64__) || defined(__arm__) + case __NR_sendmsg: // Could specify destination. + case __NR_sendto: // Could specify destination. +#endif + case __NR_write: + case __NR_writev: + return true; + case __NR_ioctl: // Can be very powerful. + case __NR_pread64: + case __NR_preadv: + case __NR_pwrite64: + case __NR_pwritev: + case __NR_recvmmsg: // Could specify source. + case __NR_sendfile: +#if defined(__i386__) || defined(__arm__) + case __NR_sendfile64: +#endif + case __NR_sendmmsg: // Could specify destination. + case __NR_splice: + case __NR_tee: + case __NR_vmsplice: + default: + return false; + } +} + +bool SyscallSets::IsAllowedPrctl(int sysno) { + switch (sysno) { + case __NR_prctl: + return true; + default: +#if defined(__x86_64__) + case __NR_arch_prctl: +#endif + return false; + } +} + +bool SyscallSets::IsAllowedBasicScheduler(int sysno) { + switch (sysno) { + case __NR_sched_yield: + case __NR_pause: + case __NR_nanosleep: + return true; + case __NR_getpriority: +#if defined(__i386__) || defined(__arm__) + case __NR_nice: +#endif + case __NR_setpriority: + default: + return false; + } +} + +bool SyscallSets::IsAdminOperation(int sysno) { + switch (sysno) { +#if defined(__i386__) || defined(__arm__) + case __NR_bdflush: +#endif + case __NR_kexec_load: + case __NR_reboot: + case __NR_setdomainname: + case __NR_sethostname: + case __NR_syslog: + return true; + default: + return false; + } +} + +bool SyscallSets::IsKernelModule(int sysno) { + switch (sysno) { +#if defined(__i386__) || defined(__x86_64__) + case __NR_create_module: + case __NR_get_kernel_syms: // Should ENOSYS. + case __NR_query_module: +#endif + case __NR_delete_module: + case __NR_init_module: + return true; + default: + return false; + } +} + +bool SyscallSets::IsGlobalFSViewChange(int sysno) { + switch (sysno) { + case __NR_pivot_root: + case __NR_chroot: + case __NR_sync: + return true; + default: + return false; + } +} + +bool SyscallSets::IsFsControl(int sysno) { + switch (sysno) { + case __NR_mount: + case __NR_nfsservctl: + case __NR_quotactl: + case __NR_swapoff: + case __NR_swapon: +#if defined(__i386__) + case __NR_umount: +#endif + case __NR_umount2: + return true; + default: + return false; + } +} + +bool SyscallSets::IsNuma(int sysno) { + switch (sysno) { + case __NR_get_mempolicy: + case __NR_getcpu: + case __NR_mbind: +#if defined(__i386__) || defined(__x86_64__) + case __NR_migrate_pages: +#endif + case __NR_move_pages: + case __NR_set_mempolicy: + return true; + default: + return false; + } +} + +bool SyscallSets::IsMessageQueue(int sysno) { + switch (sysno) { + case __NR_mq_getsetattr: + case __NR_mq_notify: + case __NR_mq_open: + case __NR_mq_timedreceive: + case __NR_mq_timedsend: + case __NR_mq_unlink: + return true; + default: + return false; + } +} + +bool SyscallSets::IsGlobalProcessEnvironment(int sysno) { + switch (sysno) { + case __NR_acct: // Privileged. +#if defined(__i386__) || defined(__x86_64__) + case __NR_getrlimit: +#endif +#if defined(__i386__) || defined(__arm__) + case __NR_ugetrlimit: +#endif +#if defined(__i386__) + case __NR_ulimit: +#endif + case __NR_getrusage: + case __NR_personality: // Can change its personality as well. + case __NR_prlimit64: // Like setrlimit / getrlimit. + case __NR_setrlimit: + case __NR_times: + return true; + default: + return false; + } +} + +bool SyscallSets::IsDebug(int sysno) { + switch (sysno) { + case __NR_ptrace: + case __NR_process_vm_readv: + case __NR_process_vm_writev: +#if defined(__i386__) || defined(__x86_64__) + case __NR_kcmp: +#endif + return true; + default: + return false; + } +} + +bool SyscallSets::IsGlobalSystemStatus(int sysno) { + switch (sysno) { + case __NR__sysctl: + case __NR_sysfs: + case __NR_sysinfo: + case __NR_uname: +#if defined(__i386__) + case __NR_olduname: + case __NR_oldolduname: +#endif + return true; + default: + return false; + } +} + +bool SyscallSets::IsEventFd(int sysno) { + switch (sysno) { + case __NR_eventfd: + case __NR_eventfd2: + return true; + default: + return false; + } +} + +// Asynchronous I/O API. +bool SyscallSets::IsAsyncIo(int sysno) { + switch (sysno) { + case __NR_io_cancel: + case __NR_io_destroy: + case __NR_io_getevents: + case __NR_io_setup: + case __NR_io_submit: + return true; + default: + return false; + } +} + +bool SyscallSets::IsKeyManagement(int sysno) { + switch (sysno) { + case __NR_add_key: + case __NR_keyctl: + case __NR_request_key: + return true; + default: + return false; + } +} + +#if defined(__x86_64__) || defined(__arm__) +bool SyscallSets::IsSystemVSemaphores(int sysno) { + switch (sysno) { + case __NR_semctl: + case __NR_semget: + case __NR_semop: + case __NR_semtimedop: + return true; + default: + return false; + } +} +#endif + +#if defined(__x86_64__) || defined(__arm__) +// These give a lot of ambient authority and bypass the setuid sandbox. +bool SyscallSets::IsSystemVSharedMemory(int sysno) { + switch (sysno) { + case __NR_shmat: + case __NR_shmctl: + case __NR_shmdt: + case __NR_shmget: + return true; + default: + return false; + } +} +#endif + +#if defined(__x86_64__) || defined(__arm__) +bool SyscallSets::IsSystemVMessageQueue(int sysno) { + switch (sysno) { + case __NR_msgctl: + case __NR_msgget: + case __NR_msgrcv: + case __NR_msgsnd: + return true; + default: + return false; + } +} +#endif + +#if defined(__i386__) +// Big system V multiplexing system call. +bool SyscallSets::IsSystemVIpc(int sysno) { + switch (sysno) { + case __NR_ipc: + return true; + default: + return false; + } +} +#endif + +bool SyscallSets::IsAnySystemV(int sysno) { +#if defined(__x86_64__) || defined(__arm__) + return IsSystemVMessageQueue(sysno) || IsSystemVSemaphores(sysno) || + IsSystemVSharedMemory(sysno); +#elif defined(__i386__) + return IsSystemVIpc(sysno); +#endif +} + +bool SyscallSets::IsAdvancedScheduler(int sysno) { + switch (sysno) { + case __NR_ioprio_get: // IO scheduler. + case __NR_ioprio_set: + case __NR_sched_get_priority_max: + case __NR_sched_get_priority_min: + case __NR_sched_getaffinity: + case __NR_sched_getparam: + case __NR_sched_getscheduler: + case __NR_sched_rr_get_interval: + case __NR_sched_setaffinity: + case __NR_sched_setparam: + case __NR_sched_setscheduler: + return true; + default: + return false; + } +} + +bool SyscallSets::IsInotify(int sysno) { + switch (sysno) { + case __NR_inotify_add_watch: + case __NR_inotify_init: + case __NR_inotify_init1: + case __NR_inotify_rm_watch: + return true; + default: + return false; + } +} + +bool SyscallSets::IsFaNotify(int sysno) { + switch (sysno) { + case __NR_fanotify_init: + case __NR_fanotify_mark: + return true; + default: + return false; + } +} + +bool SyscallSets::IsTimer(int sysno) { + switch (sysno) { + case __NR_getitimer: +#if defined(__i386__) || defined(__x86_64__) + case __NR_alarm: +#endif + case __NR_setitimer: + return true; + default: + return false; + } +} + +bool SyscallSets::IsAdvancedTimer(int sysno) { + switch (sysno) { + case __NR_timer_create: + case __NR_timer_delete: + case __NR_timer_getoverrun: + case __NR_timer_gettime: + case __NR_timer_settime: + case __NR_timerfd_create: + case __NR_timerfd_gettime: + case __NR_timerfd_settime: + return true; + default: + return false; + } +} + +bool SyscallSets::IsExtendedAttributes(int sysno) { + switch (sysno) { + case __NR_fgetxattr: + case __NR_flistxattr: + case __NR_fremovexattr: + case __NR_fsetxattr: + case __NR_getxattr: + case __NR_lgetxattr: + case __NR_listxattr: + case __NR_llistxattr: + case __NR_lremovexattr: + case __NR_lsetxattr: + case __NR_removexattr: + case __NR_setxattr: + return true; + default: + return false; + } +} + +// Various system calls that need to be researched. +// TODO(jln): classify this better. +bool SyscallSets::IsMisc(int sysno) { + switch (sysno) { + case __NR_name_to_handle_at: + case __NR_open_by_handle_at: + case __NR_perf_event_open: + case __NR_syncfs: + case __NR_vhangup: +// The system calls below are not implemented. +#if defined(__i386__) || defined(__x86_64__) + case __NR_afs_syscall: +#endif +#if defined(__i386__) + case __NR_break: +#endif +#if defined(__i386__) || defined(__x86_64__) + case __NR_getpmsg: +#endif +#if defined(__i386__) + case __NR_gtty: + case __NR_idle: + case __NR_lock: + case __NR_mpx: + case __NR_prof: + case __NR_profil: +#endif +#if defined(__i386__) || defined(__x86_64__) + case __NR_putpmsg: +#endif +#if defined(__x86_64__) + case __NR_security: +#endif +#if defined(__i386__) + case __NR_stty: +#endif +#if defined(__x86_64__) + case __NR_tuxcall: +#endif + case __NR_vserver: + return true; + default: + return false; + } +} + +#if defined(__arm__) +bool SyscallSets::IsArmPciConfig(int sysno) { + switch (sysno) { + case __NR_pciconfig_iobase: + case __NR_pciconfig_read: + case __NR_pciconfig_write: + return true; + default: + return false; + } +} + +bool SyscallSets::IsArmPrivate(int sysno) { + switch (sysno) { + case __ARM_NR_breakpoint: + case __ARM_NR_cacheflush: + case __ARM_NR_set_tls: + case __ARM_NR_usr26: + case __ARM_NR_usr32: + return true; + default: + return false; + } +} +#endif // defined(__arm__) + +} // namespace sandbox. diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h new file mode 100644 index 00000000000..d2cf1a17801 --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h @@ -0,0 +1,105 @@ +// Copyright (c) 2013 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_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_ + +#include "base/basictypes.h" +#include "build/build_config.h" + +// These are helpers to build seccomp-bpf policies, i.e. policies for a +// sandbox that reduces the Linux kernel's attack surface. Given their +// nature, they don't have any clear semantics and are completely +// "implementation-defined". + +namespace sandbox { + +class SyscallSets { + public: + static bool IsKill(int sysno); + static bool IsAllowedGettime(int sysno); + static bool IsCurrentDirectory(int sysno); + static bool IsUmask(int sysno); + // System calls that directly access the file system. They might acquire + // a new file descriptor or otherwise perform an operation directly + // via a path. + static bool IsFileSystem(int sysno); + static bool IsAllowedFileSystemAccessViaFd(int sysno); + static bool IsDeniedFileSystemAccessViaFd(int sysno); + static bool IsGetSimpleId(int sysno); + static bool IsProcessPrivilegeChange(int sysno); + static bool IsProcessGroupOrSession(int sysno); + static bool IsAllowedSignalHandling(int sysno); + static bool IsAllowedOperationOnFd(int sysno); + static bool IsKernelInternalApi(int sysno); + // This should be thought through in conjunction with IsFutex(). + static bool IsAllowedProcessStartOrDeath(int sysno); + // It's difficult to restrict those, but there is attack surface here. + static bool IsFutex(int sysno); + static bool IsAllowedEpoll(int sysno); + static bool IsAllowedGetOrModifySocket(int sysno); + static bool IsDeniedGetOrModifySocket(int sysno); + +#if defined(__i386__) + // Big multiplexing system call for sockets. + static bool IsSocketCall(int sysno); +#endif + +#if defined(__x86_64__) || defined(__arm__) + static bool IsNetworkSocketInformation(int sysno); +#endif + + static bool IsAllowedAddressSpaceAccess(int sysno); + static bool IsAllowedGeneralIo(int sysno); + static bool IsAllowedPrctl(int sysno); + static bool IsAllowedBasicScheduler(int sysno); + static bool IsAdminOperation(int sysno); + static bool IsKernelModule(int sysno); + static bool IsGlobalFSViewChange(int sysno); + static bool IsFsControl(int sysno); + static bool IsNuma(int sysno); + static bool IsMessageQueue(int sysno); + static bool IsGlobalProcessEnvironment(int sysno); + static bool IsDebug(int sysno); + static bool IsGlobalSystemStatus(int sysno); + static bool IsEventFd(int sysno); + // Asynchronous I/O API. + static bool IsAsyncIo(int sysno); + static bool IsKeyManagement(int sysno); +#if defined(__x86_64__) || defined(__arm__) + static bool IsSystemVSemaphores(int sysno); +#endif +#if defined(__x86_64__) || defined(__arm__) + // These give a lot of ambient authority and bypass the setuid sandbox. + static bool IsSystemVSharedMemory(int sysno); +#endif + +#if defined(__x86_64__) || defined(__arm__) + static bool IsSystemVMessageQueue(int sysno); +#endif + +#if defined(__i386__) + // Big system V multiplexing system call. + static bool IsSystemVIpc(int sysno); +#endif + + static bool IsAnySystemV(int sysno); + static bool IsAdvancedScheduler(int sysno); + static bool IsInotify(int sysno); + static bool IsFaNotify(int sysno); + static bool IsTimer(int sysno); + static bool IsAdvancedTimer(int sysno); + static bool IsExtendedAttributes(int sysno); + static bool IsMisc(int sysno); +#if defined(__arm__) + static bool IsArmPciConfig(int sysno); + static bool IsArmPrivate(int sysno); +#endif // defined(__arm__) + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallSets); +}; + +} // namespace sandbox. + +#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf/Makefile b/chromium/sandbox/linux/seccomp-bpf/Makefile deleted file mode 100644 index 6b355805970..00000000000 --- a/chromium/sandbox/linux/seccomp-bpf/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I. -DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -iquote ../../.. -DEF_LDFLAGS = -g -lpthread -DEPFLAGS = -MMD -MF .$@.d -MODS := demo sandbox_bpf basicblock codegen die errorcode syscall syscall_iterator trap verifier -OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/') -OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/') -ALL_OBJS = $(OBJS32) $(OBJS64) -DEP_FILES = $(wildcard $(foreach f,$(ALL_OBJS),.$(f).d)) - -.SUFFIXES: .o64 .o32 - -all: demo32 demo64 - -clean: - $(RM) demo32 demo64 - $(RM) *.o *.o32 *.o64 .*.d - $(RM) core core.* vgcore vgcore.* strace.log* - --include $(DEP_FILES) - -demo32: ${OBJS32} - ${CXX} -m32 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS} -demo64: ${OBJS64} - ${CXX} -m64 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS} - -.cc.o32: - ${CXX} -m32 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $< -.cc.o64: - ${CXX} -m64 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $< diff --git a/chromium/sandbox/linux/seccomp-bpf/basicblock.cc b/chromium/sandbox/linux/seccomp-bpf/basicblock.cc index bf27c582e29..eb857f00b6c 100644 --- a/chromium/sandbox/linux/seccomp-bpf/basicblock.cc +++ b/chromium/sandbox/linux/seccomp-bpf/basicblock.cc @@ -4,13 +4,10 @@ #include "sandbox/linux/seccomp-bpf/basicblock.h" +namespace sandbox { -namespace playground2 { +BasicBlock::BasicBlock() {} -BasicBlock::BasicBlock() { -} +BasicBlock::~BasicBlock() {} -BasicBlock::~BasicBlock() { -} - -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/basicblock.h b/chromium/sandbox/linux/seccomp-bpf/basicblock.h index 1782a8076bd..d15a372cfea 100644 --- a/chromium/sandbox/linux/seccomp-bpf/basicblock.h +++ b/chromium/sandbox/linux/seccomp-bpf/basicblock.h @@ -9,8 +9,7 @@ #include "sandbox/linux/seccomp-bpf/instruction.h" - -namespace playground2 { +namespace sandbox { struct BasicBlock { BasicBlock(); @@ -20,25 +19,24 @@ struct BasicBlock { // identify common sequences of basic blocks. This would normally be // really easy to do, but STL requires us to wrap the comparator into // a class. We begrudgingly add some code here that provides this wrapping. - template class Less { + template + class Less { public: - Less(const T& data, int (*cmp)(const BasicBlock *, const BasicBlock *, - const T& data)) - : data_(data), - cmp_(cmp) { - } + Less(const T& data, + int (*cmp)(const BasicBlock*, const BasicBlock*, const T& data)) + : data_(data), cmp_(cmp) {} - bool operator() (const BasicBlock *a, const BasicBlock *b) const { + bool operator()(const BasicBlock* a, const BasicBlock* b) const { return cmp_(a, b, data_) < 0; } private: const T& data_; - int (*cmp_)(const BasicBlock *, const BasicBlock *, const T&); + int (*cmp_)(const BasicBlock*, const BasicBlock*, const T&); }; // Basic blocks are essentially nothing more than a set of instructions. - std::vector instructions; + std::vector instructions; // In order to compute relative branch offsets we need to keep track of // how far our block is away from the very last basic block. The "offset_" @@ -46,6 +44,6 @@ struct BasicBlock { int offset; }; -} // namespace playground2 +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h index 83fc10a734f..7095c23b8c1 100644 --- a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -20,15 +20,14 @@ namespace sandbox { // macros from unit_tests.h to specify the expected error condition. // A BPF_DEATH_TEST is always disabled under ThreadSanitizer, see // crbug.com/243968. -#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux...) \ - void BPF_TEST_##test_name(sandbox::BpfTests::AuxType& BPF_AUX); \ - TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \ - sandbox::BpfTests::TestArgs arg(BPF_TEST_##test_name, policy); \ - sandbox::BpfTests::RunTestInProcess( \ - sandbox::BpfTests::TestWrapper, &arg, \ - death); \ - } \ - void BPF_TEST_##test_name(sandbox::BpfTests::AuxType& BPF_AUX) +#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux...) \ + void BPF_TEST_##test_name(sandbox::BPFTests::AuxType& BPF_AUX); \ + TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \ + sandbox::BPFTests::TestArgs arg(BPF_TEST_##test_name, policy); \ + sandbox::BPFTests::RunTestInProcess( \ + sandbox::BPFTests::TestWrapper, &arg, death); \ + } \ + void BPF_TEST_##test_name(sandbox::BPFTests::AuxType& BPF_AUX) // BPF_TEST() is a special version of SANDBOX_TEST(). It turns into a no-op, // if the host does not have kernel support for running BPF filters. @@ -38,59 +37,54 @@ namespace sandbox { // BPF_TEST() takes a C++ data type as an optional fourth parameter. If // present, this sets up a variable that can be accessed as "BPF_AUX". This // variable will be passed as an argument to the "policy" function. Policies -// would typically use it as an argument to Sandbox::Trap(), if they want to +// would typically use it as an argument to SandboxBPF::Trap(), if they want to // communicate data between the BPF_TEST() and a Trap() function. -#define BPF_TEST(test_case_name, test_name, policy, aux...) \ +#define BPF_TEST(test_case_name, test_name, policy, aux...) \ BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux) - // Assertions are handled exactly the same as with a normal SANDBOX_TEST() #define BPF_ASSERT SANDBOX_ASSERT - // The "Aux" type is optional. We use an "empty" type by default, so that if // the caller doesn't provide any type, all the BPF_AUX related data compiles // to nothing. -template -class BpfTests : public UnitTests { +template +class BPFTests : public UnitTests { public: typedef Aux AuxType; class TestArgs { public: - TestArgs(void (*t)(AuxType&), playground2::Sandbox::EvaluateSyscall p) - : test_(t), - policy_(p), - aux_() { - } + TestArgs(void (*t)(AuxType&), sandbox::SandboxBPF::EvaluateSyscall p) + : test_(t), policy_(p), aux_() {} void (*test() const)(AuxType&) { return test_; } - playground2::Sandbox::EvaluateSyscall policy() const { return policy_; } + sandbox::SandboxBPF::EvaluateSyscall policy() const { return policy_; } private: - friend class BpfTests; + friend class BPFTests; void (*test_)(AuxType&); - playground2::Sandbox::EvaluateSyscall policy_; + sandbox::SandboxBPF::EvaluateSyscall policy_; AuxType aux_; }; - static void TestWrapper(void *void_arg) { - TestArgs *arg = reinterpret_cast(void_arg); - playground2::Die::EnableSimpleExit(); - if (playground2::Sandbox::SupportsSeccompSandbox(-1) == - playground2::Sandbox::STATUS_AVAILABLE) { + static void TestWrapper(void* void_arg) { + TestArgs* arg = reinterpret_cast(void_arg); + sandbox::Die::EnableSimpleExit(); + if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) == + sandbox::SandboxBPF::STATUS_AVAILABLE) { // Ensure the the sandbox is actually available at this time int proc_fd; - BPF_ASSERT((proc_fd = open("/proc", O_RDONLY|O_DIRECTORY)) >= 0); - BPF_ASSERT(playground2::Sandbox::SupportsSeccompSandbox(proc_fd) == - playground2::Sandbox::STATUS_AVAILABLE); + BPF_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0); + BPF_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) == + sandbox::SandboxBPF::STATUS_AVAILABLE); // Initialize and then start the sandbox with our custom policy - playground2::Sandbox sandbox; + sandbox::SandboxBPF sandbox; sandbox.set_proc_fd(proc_fd); - sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_); - sandbox.Sandbox::StartSandbox(); + sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_); + sandbox.SandboxBPF::StartSandbox(); arg->test()(arg->aux_); } else { @@ -104,9 +98,9 @@ class BpfTests : public UnitTests { } // Call the compiler and verify the policy. That's the least we can do, // if we don't have kernel support. - playground2::Sandbox sandbox; - sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_); - playground2::Sandbox::Program *program = + sandbox::SandboxBPF sandbox; + sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_); + sandbox::SandboxBPF::Program* program = sandbox.AssembleFilter(true /* force_verification */); delete program; sandbox::UnitTests::IgnoreThisTest(); @@ -114,9 +108,9 @@ class BpfTests : public UnitTests { } private: - DISALLOW_IMPLICIT_CONSTRUCTORS(BpfTests); + DISALLOW_IMPLICIT_CONSTRUCTORS(BPFTests); }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.cc b/chromium/sandbox/linux/seccomp-bpf/codegen.cc index 17b5d846dc9..8fb1701179e 100644 --- a/chromium/sandbox/linux/seccomp-bpf/codegen.cc +++ b/chromium/sandbox/linux/seccomp-bpf/codegen.cc @@ -6,37 +6,34 @@ #include "sandbox/linux/seccomp-bpf/codegen.h" - namespace { // Helper function for Traverse(). -void TraverseRecursively(std::set *visited, - playground2::Instruction *instruction) { +void TraverseRecursively(std::set* visited, + sandbox::Instruction* instruction) { if (visited->find(instruction) == visited->end()) { visited->insert(instruction); switch (BPF_CLASS(instruction->code)) { - case BPF_JMP: - if (BPF_OP(instruction->code) != BPF_JA) { - TraverseRecursively(visited, instruction->jf_ptr); - } - TraverseRecursively(visited, instruction->jt_ptr); - break; - case BPF_RET: - break; - default: - TraverseRecursively(visited, instruction->next); - break; + case BPF_JMP: + if (BPF_OP(instruction->code) != BPF_JA) { + TraverseRecursively(visited, instruction->jf_ptr); + } + TraverseRecursively(visited, instruction->jt_ptr); + break; + case BPF_RET: + break; + default: + TraverseRecursively(visited, instruction->next); + break; } } } } // namespace -namespace playground2 { +namespace sandbox { -CodeGen::CodeGen() - : compiled_(false) { -} +CodeGen::CodeGen() : compiled_(false) {} CodeGen::~CodeGen() { for (Instructions::iterator iter = instructions_.begin(); @@ -51,115 +48,121 @@ CodeGen::~CodeGen() { } } -void CodeGen::PrintProgram(const Sandbox::Program& program) { - for (Sandbox::Program::const_iterator iter = program.begin(); +void CodeGen::PrintProgram(const SandboxBPF::Program& program) { + for (SandboxBPF::Program::const_iterator iter = program.begin(); iter != program.end(); ++iter) { int ip = (int)(iter - program.begin()); fprintf(stderr, "%3d) ", ip); switch (BPF_CLASS(iter->code)) { - case BPF_LD: - if (iter->code == BPF_LD+BPF_W+BPF_ABS) { - fprintf(stderr, "LOAD %d // ", (int)iter->k); - if (iter->k == offsetof(struct arch_seccomp_data, nr)) { - fprintf(stderr, "System call number\n"); - } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) { - fprintf(stderr, "Architecture\n"); - } else if (iter->k == offsetof(struct arch_seccomp_data, - instruction_pointer)) { - fprintf(stderr, "Instruction pointer (LSB)\n"); - } else if (iter->k == offsetof(struct arch_seccomp_data, - instruction_pointer) + 4) { - fprintf(stderr, "Instruction pointer (MSB)\n"); - } else if (iter->k >= offsetof(struct arch_seccomp_data, args) && - iter->k < offsetof(struct arch_seccomp_data, args)+48 && - (iter->k-offsetof(struct arch_seccomp_data, args))%4 == 0) { - fprintf(stderr, "Argument %d (%cSB)\n", - (int)(iter->k-offsetof(struct arch_seccomp_data, args))/8, - (iter->k-offsetof(struct arch_seccomp_data, - args))%8 ? 'M' : 'L'); + case BPF_LD: + if (iter->code == BPF_LD + BPF_W + BPF_ABS) { + fprintf(stderr, "LOAD %d // ", (int)iter->k); + if (iter->k == offsetof(struct arch_seccomp_data, nr)) { + fprintf(stderr, "System call number\n"); + } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) { + fprintf(stderr, "Architecture\n"); + } else if (iter->k == + offsetof(struct arch_seccomp_data, instruction_pointer)) { + fprintf(stderr, "Instruction pointer (LSB)\n"); + } else if (iter->k == + offsetof(struct arch_seccomp_data, instruction_pointer) + + 4) { + fprintf(stderr, "Instruction pointer (MSB)\n"); + } else if (iter->k >= offsetof(struct arch_seccomp_data, args) && + iter->k < offsetof(struct arch_seccomp_data, args) + 48 && + (iter->k - offsetof(struct arch_seccomp_data, args)) % 4 == + 0) { + fprintf( + stderr, + "Argument %d (%cSB)\n", + (int)(iter->k - offsetof(struct arch_seccomp_data, args)) / 8, + (iter->k - offsetof(struct arch_seccomp_data, args)) % 8 ? 'M' + : 'L'); + } else { + fprintf(stderr, "???\n"); + } + } else { + fprintf(stderr, "LOAD ???\n"); + } + break; + case BPF_JMP: + if (BPF_OP(iter->code) == BPF_JA) { + fprintf(stderr, "JMP %d\n", ip + iter->k + 1); + } else { + fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n", + BPF_OP(iter->code) == BPF_JSET ? "&" : + BPF_OP(iter->code) == BPF_JEQ ? "==" : + BPF_OP(iter->code) == BPF_JGE ? ">=" : + BPF_OP(iter->code) == BPF_JGT ? ">" : "???", + (int)iter->k, + ip + iter->jt + 1, ip + iter->jf + 1); + } + break; + case BPF_RET: + fprintf(stderr, "RET 0x%x // ", iter->k); + if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) { + fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA); + } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { + fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA); + } else if (iter->k == SECCOMP_RET_ALLOW) { + fprintf(stderr, "Allowed\n"); } else { fprintf(stderr, "???\n"); } - } else { - fprintf(stderr, "LOAD ???\n"); - } - break; - case BPF_JMP: - if (BPF_OP(iter->code) == BPF_JA) { - fprintf(stderr, "JMP %d\n", ip + iter->k + 1); - } else { - fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n", - BPF_OP(iter->code) == BPF_JSET ? "&" : - BPF_OP(iter->code) == BPF_JEQ ? "==" : - BPF_OP(iter->code) == BPF_JGE ? ">=" : - BPF_OP(iter->code) == BPF_JGT ? ">" : "???", - (int)iter->k, - ip + iter->jt + 1, ip + iter->jf + 1); - } - break; - case BPF_RET: - fprintf(stderr, "RET 0x%x // ", iter->k); - if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) { - fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA); - } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { - fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA); - } else if (iter->k == SECCOMP_RET_ALLOW) { - fprintf(stderr, "Allowed\n"); - } else { + break; + case BPF_ALU: + fprintf(stderr, BPF_OP(iter->code) == BPF_NEG + ? "A := -A\n" : "A := A %s 0x%x\n", + BPF_OP(iter->code) == BPF_ADD ? "+" : + BPF_OP(iter->code) == BPF_SUB ? "-" : + BPF_OP(iter->code) == BPF_MUL ? "*" : + BPF_OP(iter->code) == BPF_DIV ? "/" : + BPF_OP(iter->code) == BPF_MOD ? "%" : + BPF_OP(iter->code) == BPF_OR ? "|" : + BPF_OP(iter->code) == BPF_XOR ? "^" : + BPF_OP(iter->code) == BPF_AND ? "&" : + BPF_OP(iter->code) == BPF_LSH ? "<<" : + BPF_OP(iter->code) == BPF_RSH ? ">>" : "???", + (int)iter->k); + break; + default: fprintf(stderr, "???\n"); - } - break; - case BPF_ALU: - fprintf(stderr, BPF_OP(iter->code) == BPF_NEG - ? "A := -A\n" : "A := A %s 0x%x\n", - BPF_OP(iter->code) == BPF_ADD ? "+" : - BPF_OP(iter->code) == BPF_SUB ? "-" : - BPF_OP(iter->code) == BPF_MUL ? "*" : - BPF_OP(iter->code) == BPF_DIV ? "/" : - BPF_OP(iter->code) == BPF_MOD ? "%" : - BPF_OP(iter->code) == BPF_OR ? "|" : - BPF_OP(iter->code) == BPF_XOR ? "^" : - BPF_OP(iter->code) == BPF_AND ? "&" : - BPF_OP(iter->code) == BPF_LSH ? "<<" : - BPF_OP(iter->code) == BPF_RSH ? ">>" : "???", - (int)iter->k); - break; - default: - fprintf(stderr, "???\n"); - break; + break; } } return; } -Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k, - Instruction *next) { +Instruction* CodeGen::MakeInstruction(uint16_t code, + uint32_t k, + Instruction* next) { // We can handle non-jumping instructions and "always" jumps. Both of // them are followed by exactly one "next" instruction. // We allow callers to defer specifying "next", but then they must call // "joinInstructions" later. if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) { - SANDBOX_DIE("Must provide both \"true\" and \"false\" branch " - "for a BPF_JMP"); + SANDBOX_DIE( + "Must provide both \"true\" and \"false\" branch " + "for a BPF_JMP"); } if (next && BPF_CLASS(code) == BPF_RET) { SANDBOX_DIE("Cannot append instructions after a return statement"); } if (BPF_CLASS(code) == BPF_JMP) { // "Always" jumps use the "true" branch target, only. - Instruction *insn = new Instruction(code, 0, next, NULL); + Instruction* insn = new Instruction(code, 0, next, NULL); instructions_.push_back(insn); return insn; } else { // Non-jumping instructions do not use any of the branch targets. - Instruction *insn = new Instruction(code, k, next); + Instruction* insn = new Instruction(code, k, next); instructions_.push_back(insn); return insn; } } -Instruction *CodeGen::MakeInstruction(uint16_t code, const ErrorCode& err) { +Instruction* CodeGen::MakeInstruction(uint16_t code, const ErrorCode& err) { if (BPF_CLASS(code) != BPF_RET) { SANDBOX_DIE("ErrorCodes can only be used in return expressions"); } @@ -170,8 +173,10 @@ Instruction *CodeGen::MakeInstruction(uint16_t code, const ErrorCode& err) { return MakeInstruction(code, err.err_); } -Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k, - Instruction *jt, Instruction *jf) { +Instruction* CodeGen::MakeInstruction(uint16_t code, + uint32_t k, + Instruction* jt, + Instruction* jf) { // We can handle all conditional jumps. They are followed by both a // "true" and a "false" branch. if (BPF_CLASS(code) != BPF_JMP || BPF_OP(code) == BPF_JA) { @@ -182,12 +187,12 @@ Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k, // targets. It must then be set later by calling "JoinInstructions". SANDBOX_DIE("Branches must jump to a valid instruction"); } - Instruction *insn = new Instruction(code, k, jt, jf); + Instruction* insn = new Instruction(code, k, jt, jf); instructions_.push_back(insn); return insn; } -void CodeGen::JoinInstructions(Instruction *head, Instruction *tail) { +void CodeGen::JoinInstructions(Instruction* head, Instruction* tail) { // Merge two instructions, or set the branch target for an "always" jump. // This function should be called, if the caller didn't initially provide // a value for "next" when creating the instruction. @@ -216,11 +221,12 @@ void CodeGen::JoinInstructions(Instruction *head, Instruction *tail) { return; } -void CodeGen::Traverse(Instruction *instruction, - void (*fnc)(Instruction *, void *), void *aux) { - std::set visited; +void CodeGen::Traverse(Instruction* instruction, + void (*fnc)(Instruction*, void*), + void* aux) { + std::set visited; TraverseRecursively(&visited, instruction); - for (std::set::const_iterator iter = visited.begin(); + for (std::set::const_iterator iter = visited.begin(); iter != visited.end(); ++iter) { fnc(*iter, aux); @@ -228,15 +234,15 @@ void CodeGen::Traverse(Instruction *instruction, } void CodeGen::FindBranchTargets(const Instruction& instructions, - BranchTargets *branch_targets) { + BranchTargets* branch_targets) { // Follow all possible paths through the "instructions" graph and compute // a list of branch targets. This will later be needed to compute the // boundaries of basic blocks. // We maintain a set of all instructions that we have previously seen. This // set ultimately converges on all instructions in the program. - std::set seen_instructions; + std::set seen_instructions; Instructions stack; - for (const Instruction *insn = &instructions; insn; ) { + for (const Instruction* insn = &instructions; insn;) { seen_instructions.insert(insn); if (BPF_CLASS(insn->code) == BPF_JMP) { // Found a jump. Increase count of incoming edges for each of the jump @@ -244,7 +250,7 @@ void CodeGen::FindBranchTargets(const Instruction& instructions, ++(*branch_targets)[insn->jt_ptr]; if (BPF_OP(insn->code) != BPF_JA) { ++(*branch_targets)[insn->jf_ptr]; - stack.push_back(const_cast(insn)); + stack.push_back(const_cast(insn)); } // Start a recursive decent for depth-first traversal. if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) { @@ -262,8 +268,9 @@ void CodeGen::FindBranchTargets(const Instruction& instructions, // (if any). It's OK if "insn" becomes NULL when reaching a return // instruction. if (!insn->next != (BPF_CLASS(insn->code) == BPF_RET)) { - SANDBOX_DIE("Internal compiler error; return instruction must be at " - "the end of the BPF program"); + SANDBOX_DIE( + "Internal compiler error; return instruction must be at " + "the end of the BPF program"); } if (seen_instructions.find(insn->next) == seen_instructions.end()) { insn = insn->next; @@ -288,8 +295,9 @@ void CodeGen::FindBranchTargets(const Instruction& instructions, // We have seen both the "true" and the "false" branch, continue // up the stack. if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) { - SANDBOX_DIE("Internal compiler error; cannot find all " - "branch targets"); + SANDBOX_DIE( + "Internal compiler error; cannot find all " + "branch targets"); } insn = NULL; } @@ -298,11 +306,10 @@ void CodeGen::FindBranchTargets(const Instruction& instructions, return; } -BasicBlock *CodeGen::MakeBasicBlock(Instruction *head, - Instruction *tail) { +BasicBlock* CodeGen::MakeBasicBlock(Instruction* head, Instruction* tail) { // Iterate over all the instructions between "head" and "tail" and // insert them into a new basic block. - BasicBlock *bb = new BasicBlock; + BasicBlock* bb = new BasicBlock; for (;; head = head->next) { bb->instructions.push_back(head); if (head == tail) { @@ -316,20 +323,21 @@ BasicBlock *CodeGen::MakeBasicBlock(Instruction *head, return bb; } -void CodeGen::AddBasicBlock(Instruction *head, - Instruction *tail, +void CodeGen::AddBasicBlock(Instruction* head, + Instruction* tail, const BranchTargets& branch_targets, - TargetsToBlocks *basic_blocks, - BasicBlock **firstBlock) { + TargetsToBlocks* basic_blocks, + BasicBlock** firstBlock) { // Add a new basic block to "basic_blocks". Also set "firstBlock", if it // has not been set before. BranchTargets::const_iterator iter = branch_targets.find(head); if ((iter == branch_targets.end()) != !*firstBlock || !*firstBlock != basic_blocks->empty()) { - SANDBOX_DIE("Only the very first basic block should have no " - "incoming jumps"); + SANDBOX_DIE( + "Only the very first basic block should have no " + "incoming jumps"); } - BasicBlock *bb = MakeBasicBlock(head, tail); + BasicBlock* bb = MakeBasicBlock(head, tail); if (!*firstBlock) { *firstBlock = bb; } @@ -337,19 +345,20 @@ void CodeGen::AddBasicBlock(Instruction *head, return; } -BasicBlock *CodeGen::CutGraphIntoBasicBlocks( - Instruction *instructions, const BranchTargets& branch_targets, - TargetsToBlocks *basic_blocks) { +BasicBlock* CodeGen::CutGraphIntoBasicBlocks( + Instruction* instructions, + const BranchTargets& branch_targets, + TargetsToBlocks* basic_blocks) { // Textbook implementation of a basic block generator. All basic blocks // start with a branch target and end with either a return statement or // a jump (or are followed by an instruction that forms the beginning of a // new block). Both conditional and "always" jumps are supported. - BasicBlock *first_block = NULL; - std::set seen_instructions; + BasicBlock* first_block = NULL; + std::set seen_instructions; Instructions stack; - Instruction *tail = NULL; - Instruction *head = instructions; - for (Instruction *insn = head; insn; ) { + Instruction* tail = NULL; + Instruction* head = instructions; + for (Instruction* insn = head; insn;) { if (seen_instructions.find(insn) != seen_instructions.end()) { // We somehow went in a circle. This should never be possible. Not even // cyclic graphs are supposed to confuse us this much. @@ -410,7 +419,8 @@ BasicBlock *CodeGen::CutGraphIntoBasicBlocks( // used in a "less" comparator for the purpose of storing pointers to basic // blocks in STL containers; this gives an easy option to use STL to find // shared tail sequences of basic blocks. -static int PointerCompare(const BasicBlock *block1, const BasicBlock *block2, +static int PointerCompare(const BasicBlock* block1, + const BasicBlock* block2, const TargetsToBlocks& blocks) { // Return <0, 0, or >0 depending on the ordering of "block1" and "block2". // If we are looking at the exact same block, this is trivial and we don't @@ -486,7 +496,7 @@ static int PointerCompare(const BasicBlock *block1, const BasicBlock *block2, } } -void CodeGen::MergeTails(TargetsToBlocks *blocks) { +void CodeGen::MergeTails(TargetsToBlocks* blocks) { // We enter all of our basic blocks into a set using the BasicBlock::Less() // comparator. This naturally results in blocks with identical tails of // instructions to map to the same entry in the set. Whenever we discover @@ -500,12 +510,11 @@ void CodeGen::MergeTails(TargetsToBlocks *blocks) { // the future, we might decide to revisit this decision and attempt to // merge arbitrary sub-sequences of instructions. BasicBlock::Less less(*blocks, PointerCompare); - typedef std::set > Set; + typedef std::set > Set; Set seen_basic_blocks(less); - for (TargetsToBlocks::iterator iter = blocks->begin(); - iter != blocks->end(); + for (TargetsToBlocks::iterator iter = blocks->begin(); iter != blocks->end(); ++iter) { - BasicBlock *bb = iter->second; + BasicBlock* bb = iter->second; Set::const_iterator entry = seen_basic_blocks.find(bb); if (entry == seen_basic_blocks.end()) { // This is the first time we see this particular sequence of @@ -521,34 +530,36 @@ void CodeGen::MergeTails(TargetsToBlocks *blocks) { } } -void CodeGen::ComputeIncomingBranches(BasicBlock *block, +void CodeGen::ComputeIncomingBranches(BasicBlock* block, const TargetsToBlocks& targets_to_blocks, - IncomingBranches *incoming_branches) { + IncomingBranches* incoming_branches) { // We increment the number of incoming branches each time we encounter a // basic block. But we only traverse recursively the very first time we // encounter a new block. This is necessary to make topological sorting // work correctly. if (++(*incoming_branches)[block] == 1) { - Instruction *last_insn = block->instructions.back(); + Instruction* last_insn = block->instructions.back(); if (BPF_CLASS(last_insn->code) == BPF_JMP) { - ComputeIncomingBranches( - targets_to_blocks.find(last_insn->jt_ptr)->second, - targets_to_blocks, incoming_branches); + ComputeIncomingBranches(targets_to_blocks.find(last_insn->jt_ptr)->second, + targets_to_blocks, + incoming_branches); if (BPF_OP(last_insn->code) != BPF_JA) { ComputeIncomingBranches( - targets_to_blocks.find(last_insn->jf_ptr)->second, - targets_to_blocks, incoming_branches); + targets_to_blocks.find(last_insn->jf_ptr)->second, + targets_to_blocks, + incoming_branches); } } else if (BPF_CLASS(last_insn->code) != BPF_RET) { ComputeIncomingBranches(targets_to_blocks.find(last_insn->next)->second, - targets_to_blocks, incoming_branches); + targets_to_blocks, + incoming_branches); } } } -void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block, +void CodeGen::TopoSortBasicBlocks(BasicBlock* first_block, const TargetsToBlocks& blocks, - BasicBlocks *basic_blocks) { + BasicBlocks* basic_blocks) { // Textbook implementation of a toposort. We keep looking for basic blocks // that don't have any incoming branches (initially, this is just the // "first_block") and add them to the topologically sorted list of @@ -562,7 +573,7 @@ void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block, IncomingBranches unordered_blocks; ComputeIncomingBranches(first_block, blocks, &unordered_blocks); - std::set heads; + std::set heads; for (;;) { // Move block from "unordered_blocks" to "basic_blocks". basic_blocks->push_back(first_block); @@ -570,7 +581,7 @@ void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block, // Inspect last instruction in the basic block. This is typically either a // jump or a return statement. But it could also be a "normal" instruction // that is followed by a jump target. - Instruction *last_insn = first_block->instructions.back(); + Instruction* last_insn = first_block->instructions.back(); if (BPF_CLASS(last_insn->code) == BPF_JMP) { // Remove outgoing branches. This might end up moving our descendants // into set of "head" nodes that no longer have any incoming branches. @@ -598,7 +609,7 @@ void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block, // Our basic block is supposed to be followed by "last_insn->next", // but dependencies prevent this from happening. Insert a BPF_JA // instruction to correct the code flow. - Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, last_insn->next); + Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, last_insn->next); first_block->instructions.push_back(ja); last_insn->next = ja; } @@ -616,7 +627,7 @@ void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block, } } -void CodeGen::ComputeRelativeJumps(BasicBlocks *basic_blocks, +void CodeGen::ComputeRelativeJumps(BasicBlocks* basic_blocks, const TargetsToBlocks& targets_to_blocks) { // While we previously used pointers in jt_ptr and jf_ptr to link jump // instructions to their targets, we now convert these jumps to relative @@ -626,38 +637,37 @@ void CodeGen::ComputeRelativeJumps(BasicBlocks *basic_blocks, // Since we just completed a toposort, all jump targets are guaranteed to // go forward. This means, iterating over the basic blocks in reverse makes // it trivial to compute the correct offsets. - BasicBlock *bb = NULL; - BasicBlock *last_bb = NULL; + BasicBlock* bb = NULL; + BasicBlock* last_bb = NULL; for (BasicBlocks::reverse_iterator iter = basic_blocks->rbegin(); iter != basic_blocks->rend(); ++iter) { last_bb = bb; bb = *iter; - Instruction *insn = bb->instructions.back(); + Instruction* insn = bb->instructions.back(); if (BPF_CLASS(insn->code) == BPF_JMP) { // Basic block ended in a jump instruction. We can now compute the // appropriate offsets. if (BPF_OP(insn->code) == BPF_JA) { // "Always" jumps use the 32bit "k" field for the offset, instead // of the 8bit "jt" and "jf" fields. - int jmp = - offset - targets_to_blocks.find(insn->jt_ptr)->second->offset; - insn->k = jmp; + int jmp = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset; + insn->k = jmp; insn->jt = insn->jf = 0; } else { // The offset computations for conditional jumps are just the same // as for "always" jumps. - int jt = offset-targets_to_blocks.find(insn->jt_ptr)->second->offset; - int jf = offset-targets_to_blocks.find(insn->jf_ptr)->second->offset; + int jt = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset; + int jf = offset - targets_to_blocks.find(insn->jf_ptr)->second->offset; // There is an added complication, because conditional relative jumps // can only jump at most 255 instructions forward. If we have to jump // further, insert an extra "always" jump. Instructions::size_type jmp = bb->instructions.size(); if (jt > 255 || (jt == 255 && jf > 255)) { - Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, insn->jt_ptr); + Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jt_ptr); bb->instructions.push_back(ja); - ja->k = jt; + ja->k = jt; ja->jt = ja->jf = 0; // The newly inserted "always" jump, of course, requires us to adjust @@ -666,9 +676,9 @@ void CodeGen::ComputeRelativeJumps(BasicBlocks *basic_blocks, ++jf; } if (jf > 255) { - Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, insn->jf_ptr); + Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jf_ptr); bb->instructions.insert(bb->instructions.begin() + jmp, ja); - ja->k = jf; + ja->k = jf; ja->jt = ja->jf = 0; // Again, we have to adjust the jump targets in the original @@ -696,7 +706,7 @@ void CodeGen::ComputeRelativeJumps(BasicBlocks *basic_blocks, } void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks, - Sandbox::Program *program) { + SandboxBPF::Program* program) { // Our basic blocks have been sorted and relative jump offsets have been // computed. The last remaining step is for all the instructions in our // basic blocks to be concatenated into a BPF program. @@ -710,24 +720,25 @@ void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks, ++insn_iter) { const Instruction& insn = **insn_iter; program->push_back( - (struct sock_filter) { insn.code, insn.jt, insn.jf, insn.k }); + (struct sock_filter) {insn.code, insn.jt, insn.jf, insn.k}); } } return; } -void CodeGen::Compile(Instruction *instructions, Sandbox::Program *program) { +void CodeGen::Compile(Instruction* instructions, SandboxBPF::Program* program) { if (compiled_) { - SANDBOX_DIE("Cannot call Compile() multiple times. Create a new code " - "generator instead"); + SANDBOX_DIE( + "Cannot call Compile() multiple times. Create a new code " + "generator instead"); } compiled_ = true; BranchTargets branch_targets; FindBranchTargets(*instructions, &branch_targets); TargetsToBlocks all_blocks; - BasicBlock *first_block = - CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks); + BasicBlock* first_block = + CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks); MergeTails(&all_blocks); BasicBlocks basic_blocks; TopoSortBasicBlocks(first_block, all_blocks, &basic_blocks); @@ -736,4 +747,4 @@ void CodeGen::Compile(Instruction *instructions, Sandbox::Program *program) { return; } -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.h b/chromium/sandbox/linux/seccomp-bpf/codegen.h index 88521c2b52d..2745e51194e 100644 --- a/chromium/sandbox/linux/seccomp-bpf/codegen.h +++ b/chromium/sandbox/linux/seccomp-bpf/codegen.h @@ -13,14 +13,13 @@ #include "sandbox/linux/seccomp-bpf/instruction.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +namespace sandbox { -namespace playground2 { - -typedef std::vector Instructions; -typedef std::vector BasicBlocks; -typedef std::map BranchTargets; -typedef std::map TargetsToBlocks; -typedef std::map IncomingBranches; +typedef std::vector Instructions; +typedef std::vector BasicBlocks; +typedef std::map BranchTargets; +typedef std::map TargetsToBlocks; +typedef std::map IncomingBranches; // The code generator instantiates a basic compiler that can convert a // graph of BPF instructions into a well-formed stream of BPF instructions. @@ -29,7 +28,7 @@ typedef std::map IncomingBranches; // // Callers would typically create a new CodeGen object and then use it to // build a DAG of Instructions. They'll eventually call Compile() to convert -// this DAG to a Sandbox::Program. +// this DAG to a SandboxBPF::Program. // // Instructions can be chained at the time when they are created, or they // can be joined later by calling JoinInstructions(). @@ -47,7 +46,7 @@ typedef std::map IncomingBranches; // // // Simplified code follows; in practice, it is important to avoid calling // // any C++ destructors after starting the sandbox. -// Sandbox::Program program; +// SandboxBPF::Program program; // gen.Compile(dag, program); // const struct sock_fprog prog = { // static_cast(program->size()), &program[0] }; @@ -60,22 +59,25 @@ class CodeGen { // This is a helper method that can be used for debugging purposes. It is // not normally called. - static void PrintProgram(const Sandbox::Program& program); + static void PrintProgram(const SandboxBPF::Program& program); // Create a new instruction. Instructions form a DAG. The instruction objects // are owned by the CodeGen object. They do not need to be explicitly // deleted. // For details on the possible parameters refer to - Instruction *MakeInstruction(uint16_t code, uint32_t k, - Instruction *next = NULL); - Instruction *MakeInstruction(uint16_t code, const ErrorCode& err); - Instruction *MakeInstruction(uint16_t code, uint32_t k, - Instruction *jt, Instruction *jf); + Instruction* MakeInstruction(uint16_t code, + uint32_t k, + Instruction* next = NULL); + Instruction* MakeInstruction(uint16_t code, const ErrorCode& err); + Instruction* MakeInstruction(uint16_t code, + uint32_t k, + Instruction* jt, + Instruction* jf); // Join two (sequences of) instructions. This is useful, if the "next" // parameter had not originally been given in the call to MakeInstruction(), // or if a (conditional) jump still has an unsatisfied target. - void JoinInstructions(Instruction *head, Instruction *tail); + void JoinInstructions(Instruction* head, Instruction* tail); // Traverse the graph of instructions and visit each instruction once. // Traversal order is implementation-defined. It is acceptable to make @@ -83,74 +85,75 @@ class CodeGen { // do not affect traversal. // The "fnc" function gets called with both the instruction and the opaque // "aux" pointer. - void Traverse(Instruction *, void (*fnc)(Instruction *, void *aux), - void *aux); + void Traverse(Instruction*, void (*fnc)(Instruction*, void* aux), void* aux); // Compiles the graph of instructions into a BPF program that can be passed // to the kernel. Please note that this function modifies the graph in place // and must therefore only be called once per graph. - void Compile(Instruction *instructions, Sandbox::Program *program); + void Compile(Instruction* instructions, SandboxBPF::Program* program); private: friend class CodeGenUnittestHelper; // Find all the instructions that are the target of BPF_JMPs. void FindBranchTargets(const Instruction& instructions, - BranchTargets *branch_targets); + BranchTargets* branch_targets); // Combine instructions between "head" and "tail" into a new basic block. // Basic blocks are defined as sequences of instructions whose only branch // target is the very first instruction; furthermore, any BPF_JMP or BPF_RET // instruction must be at the very end of the basic block. - BasicBlock *MakeBasicBlock(Instruction *head, Instruction *tail); + BasicBlock* MakeBasicBlock(Instruction* head, Instruction* tail); // Creates a basic block and adds it to "basic_blocks"; sets "first_block" // if it is still NULL. - void AddBasicBlock(Instruction *head, Instruction *tail, + void AddBasicBlock(Instruction* head, + Instruction* tail, const BranchTargets& branch_targets, - TargetsToBlocks *basic_blocks, BasicBlock **first_block); + TargetsToBlocks* basic_blocks, + BasicBlock** first_block); // Cuts the DAG of instructions into basic blocks. - BasicBlock *CutGraphIntoBasicBlocks(Instruction *instructions, + BasicBlock* CutGraphIntoBasicBlocks(Instruction* instructions, const BranchTargets& branch_targets, - TargetsToBlocks *blocks); + TargetsToBlocks* blocks); // Find common tail sequences of basic blocks and coalesce them. - void MergeTails(TargetsToBlocks *blocks); + void MergeTails(TargetsToBlocks* blocks); // For each basic block, compute the number of incoming branches. - void ComputeIncomingBranches(BasicBlock *block, + void ComputeIncomingBranches(BasicBlock* block, const TargetsToBlocks& targets_to_blocks, - IncomingBranches *incoming_branches); + IncomingBranches* incoming_branches); // Topologically sort the basic blocks so that all jumps are forward jumps. // This is a requirement for any well-formed BPF program. - void TopoSortBasicBlocks(BasicBlock *first_block, + void TopoSortBasicBlocks(BasicBlock* first_block, const TargetsToBlocks& blocks, - BasicBlocks *basic_blocks); + BasicBlocks* basic_blocks); // Convert jt_ptr_ and jf_ptr_ fields in BPF_JMP instructions to valid // jt_ and jf_ jump offsets. This can result in BPF_JA instructions being // inserted, if we need to jump over more than 256 instructions. - void ComputeRelativeJumps(BasicBlocks *basic_blocks, + void ComputeRelativeJumps(BasicBlocks* basic_blocks, const TargetsToBlocks& targets_to_blocks); // Concatenate instructions from all basic blocks into a BPF program that // can be passed to the kernel. - void ConcatenateBasicBlocks(const BasicBlocks&, Sandbox::Program *program); + void ConcatenateBasicBlocks(const BasicBlocks&, SandboxBPF::Program* program); // We stick all instructions and basic blocks into pools that get destroyed // when the CodeGen object is destroyed. This way, we neither need to worry // about explicitly managing ownership, nor do we need to worry about using // smart pointers in the presence of circular references. Instructions instructions_; - BasicBlocks basic_blocks_; + BasicBlocks basic_blocks_; // Compile() must only ever be called once as it makes destructive changes // to the DAG. bool compiled_; }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc index d24bcf280a6..0539a0d4337 100644 --- a/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include #include @@ -10,11 +12,11 @@ #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/tests/unit_tests.h" -namespace playground2 { +namespace sandbox { -class SandboxUnittestHelper : public Sandbox { +class SandboxUnittestHelper : public SandboxBPF { public: - typedef Sandbox::Program Program; + typedef SandboxBPF::Program Program; }; // We want to access some of the private methods in the code generator. We @@ -442,4 +444,4 @@ SANDBOX_TEST(CodeGen, All) { ForAllPrograms(CompileAndCompare); } -} // namespace playground2 +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/demo.cc b/chromium/sandbox/linux/seccomp-bpf/demo.cc index b2622ec452d..14180181a6e 100644 --- a/chromium/sandbox/linux/seccomp-bpf/demo.cc +++ b/chromium/sandbox/linux/seccomp-bpf/demo.cc @@ -26,11 +26,13 @@ #include #include +#include "base/posix/eintr_wrapper.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/services/linux_syscalls.h" -using playground2::arch_seccomp_data; -using playground2::ErrorCode; -using playground2::Sandbox; +using sandbox::ErrorCode; +using sandbox::SandboxBPF; +using sandbox::arch_seccomp_data; #define ERR EPERM @@ -221,7 +223,7 @@ intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) { char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)]; *buf = '\000'; - strncat(buf, msg0, sizeof(buf)); + strncat(buf, msg0, sizeof(buf) - 1); char *ptr = strrchr(buf, '\000'); itoa_r(data.nr, ptr, sizeof(buf) - (ptr - buf)); @@ -235,7 +237,7 @@ intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) { return -ERR; } -ErrorCode Evaluator(Sandbox *sandbox, int sysno, void *) { +ErrorCode Evaluator(SandboxBPF* sandbox, int sysno, void *) { switch (sysno) { #if defined(__NR_accept) case __NR_accept: case __NR_accept4: @@ -411,14 +413,14 @@ int main(int argc, char *argv[]) { if (argc) { } if (argv) { } int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY); - if (Sandbox::SupportsSeccompSandbox(proc_fd) != - Sandbox::STATUS_AVAILABLE) { + if (SandboxBPF::SupportsSeccompSandbox(proc_fd) != + SandboxBPF::STATUS_AVAILABLE) { perror("sandbox"); _exit(1); } - Sandbox sandbox; + SandboxBPF sandbox; sandbox.set_proc_fd(proc_fd); - sandbox.SetSandboxPolicy(Evaluator, NULL); + sandbox.SetSandboxPolicyDeprecated(Evaluator, NULL); sandbox.StartSandbox(); // Check that we can create threads diff --git a/chromium/sandbox/linux/seccomp-bpf/die.cc b/chromium/sandbox/linux/seccomp-bpf/die.cc index dfc59a50e78..533e2e9c353 100644 --- a/chromium/sandbox/linux/seccomp-bpf/die.cc +++ b/chromium/sandbox/linux/seccomp-bpf/die.cc @@ -9,11 +9,12 @@ #include +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/seccomp-bpf/syscall.h" - -namespace playground2 { +namespace sandbox { void Die::ExitGroup() { // exit_group() should exit our program. After all, it is defined as a @@ -29,8 +30,9 @@ void Die::ExitGroup() { // succeeded in doing so. Nonetheless, triggering a fatal signal could help // us terminate. signal(SIGSEGV, SIG_DFL); - SandboxSyscall(__NR_prctl, PR_SET_DUMPABLE, (void *)0, (void *)0, (void *)0); - if (*(volatile char *)0) { } + SandboxSyscall(__NR_prctl, PR_SET_DUMPABLE, (void*)0, (void*)0, (void*)0); + if (*(volatile char*)0) { + } // If there is no way for us to ask for the program to exit, the next // best thing we can do is to loop indefinitely. Maybe, somebody will notice @@ -42,37 +44,29 @@ void Die::ExitGroup() { } } -void Die::SandboxDie(const char *msg, const char *file, int line) { +void Die::SandboxDie(const char* msg, const char* file, int line) { if (simple_exit_) { LogToStderr(msg, file, line); } else { - #if defined(SECCOMP_BPF_STANDALONE) - Die::LogToStderr(msg, file, line); - #else - logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg; - #endif + logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg; } ExitGroup(); } -void Die::RawSandboxDie(const char *msg) { +void Die::RawSandboxDie(const char* msg) { if (!msg) msg = ""; RAW_LOG(FATAL, msg); ExitGroup(); } -void Die::SandboxInfo(const char *msg, const char *file, int line) { +void Die::SandboxInfo(const char* msg, const char* file, int line) { if (!suppress_info_) { - #if defined(SECCOMP_BPF_STANDALONE) - Die::LogToStderr(msg, file, line); - #else logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg; - #endif } } -void Die::LogToStderr(const char *msg, const char *file, int line) { +void Die::LogToStderr(const char* msg, const char* file, int line) { if (msg) { char buf[40]; snprintf(buf, sizeof(buf), "%d", line); @@ -80,11 +74,12 @@ void Die::LogToStderr(const char *msg, const char *file, int line) { // No need to loop. Short write()s are unlikely and if they happen we // probably prefer them over a loop that blocks. - if (HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length()))) { } + ignore_result( + HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length()))); } } -bool Die::simple_exit_ = false; +bool Die::simple_exit_ = false; bool Die::suppress_info_ = false; -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/die.h b/chromium/sandbox/linux/seccomp-bpf/die.h index 7c95997c9c8..5dcfda0f2b4 100644 --- a/chromium/sandbox/linux/seccomp-bpf/die.h +++ b/chromium/sandbox/linux/seccomp-bpf/die.h @@ -5,24 +5,23 @@ #ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__ #define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__ -#include "sandbox/linux/seccomp-bpf/port.h" +#include "base/basictypes.h" +namespace sandbox { -namespace playground2 { +// This is the main API for using this file. Prints a error message and +// exits with a fatal error. This is not async-signal safe. +#define SANDBOX_DIE(m) sandbox::Die::SandboxDie(m, __FILE__, __LINE__) -class Die { - public: - // This is the main API for using this file. Prints a error message and - // exits with a fatal error. This is not async-signal safe. - #define SANDBOX_DIE(m) playground2::Die::SandboxDie(m, __FILE__, __LINE__) - - // An async signal safe version of the same API. Won't print the filename - // and line numbers. - #define RAW_SANDBOX_DIE(m) playground2::Die::RawSandboxDie(m) +// An async signal safe version of the same API. Won't print the filename +// and line numbers. +#define RAW_SANDBOX_DIE(m) sandbox::Die::RawSandboxDie(m) - // Adds an informational message to the log file or stderr as appropriate. - #define SANDBOX_INFO(m) playground2::Die::SandboxInfo(m, __FILE__, __LINE__) +// Adds an informational message to the log file or stderr as appropriate. +#define SANDBOX_INFO(m) sandbox::Die::SandboxInfo(m, __FILE__, __LINE__) +class Die { + public: // Terminate the program, even if the current sandbox policy prevents some // of the more commonly used functions used for exiting. // Most users would want to call SANDBOX_DIE() instead, as it logs extra @@ -32,18 +31,18 @@ class Die { // This method gets called by SANDBOX_DIE(). There is normally no reason // to call it directly unless you are defining your own exiting macro. - static void SandboxDie(const char *msg, const char *file, int line) - __attribute__((noreturn)); + static void SandboxDie(const char* msg, const char* file, int line) + __attribute__((noreturn)); - static void RawSandboxDie(const char *msg) __attribute__((noreturn)); + static void RawSandboxDie(const char* msg) __attribute__((noreturn)); // This method gets called by SANDBOX_INFO(). There is normally no reason // to call it directly unless you are defining your own logging macro. - static void SandboxInfo(const char *msg, const char *file, int line); + static void SandboxInfo(const char* msg, const char* file, int line); // Writes a message to stderr. Used as a fall-back choice, if we don't have // any other way to report an error. - static void LogToStderr(const char *msg, const char *file, int line); + static void LogToStderr(const char* msg, const char* file, int line); // We generally want to run all exit handlers. This means, on SANDBOX_DIE() // we should be calling LOG(FATAL). But there are some situations where @@ -63,6 +62,6 @@ class Die { DISALLOW_IMPLICIT_CONSTRUCTORS(Die); }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_DIE_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode.cc b/chromium/sandbox/linux/seccomp-bpf/errorcode.cc index ab89d73c3fa..64848528202 100644 --- a/chromium/sandbox/linux/seccomp-bpf/errorcode.cc +++ b/chromium/sandbox/linux/seccomp-bpf/errorcode.cc @@ -5,35 +5,36 @@ #include "sandbox/linux/seccomp-bpf/die.h" #include "sandbox/linux/seccomp-bpf/errorcode.h" - -namespace playground2 { +namespace sandbox { ErrorCode::ErrorCode(int err) { switch (err) { - case ERR_ALLOWED: - err_ = SECCOMP_RET_ALLOW; - error_type_ = ET_SIMPLE; - break; - case ERR_MIN_ERRNO ... ERR_MAX_ERRNO: - err_ = SECCOMP_RET_ERRNO + err; - error_type_ = ET_SIMPLE; - break; - default: - SANDBOX_DIE("Invalid use of ErrorCode object"); + case ERR_ALLOWED: + err_ = SECCOMP_RET_ALLOW; + error_type_ = ET_SIMPLE; + break; + case ERR_MIN_ERRNO... ERR_MAX_ERRNO: + err_ = SECCOMP_RET_ERRNO + err; + error_type_ = ET_SIMPLE; + break; + default: + SANDBOX_DIE("Invalid use of ErrorCode object"); } } -ErrorCode::ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe, - uint16_t id) +ErrorCode::ErrorCode(Trap::TrapFnc fnc, const void* aux, bool safe, uint16_t id) : error_type_(ET_TRAP), fnc_(fnc), - aux_(const_cast(aux)), + aux_(const_cast(aux)), safe_(safe), - err_(SECCOMP_RET_TRAP + id) { -} + err_(SECCOMP_RET_TRAP + id) {} -ErrorCode::ErrorCode(int argno, ArgType width, Operation op, uint64_t value, - const ErrorCode *passed, const ErrorCode *failed) +ErrorCode::ErrorCode(int argno, + ArgType width, + Operation op, + uint64_t value, + const ErrorCode* passed, + const ErrorCode* failed) : error_type_(ET_COND), value_(value), argno_(argno), @@ -57,12 +58,9 @@ bool ErrorCode::Equals(const ErrorCode& err) const { if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) { return err_ == err.err_; } else if (error_type_ == ET_COND) { - return value_ == err.value_ && - argno_ == err.argno_ && - width_ == err.width_ && - op_ == err.op_ && - passed_->Equals(*err.passed_) && - failed_->Equals(*err.failed_); + return value_ == err.value_ && argno_ == err.argno_ && + width_ == err.width_ && op_ == err.op_ && + passed_->Equals(*err.passed_) && failed_->Equals(*err.failed_); } else { SANDBOX_DIE("Corrupted ErrorCode"); } @@ -103,4 +101,4 @@ bool ErrorCode::LessThan(const ErrorCode& err) const { } } -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode.h b/chromium/sandbox/linux/seccomp-bpf/errorcode.h index 61ec11013da..625d123513f 100644 --- a/chromium/sandbox/linux/seccomp-bpf/errorcode.h +++ b/chromium/sandbox/linux/seccomp-bpf/errorcode.h @@ -8,7 +8,7 @@ #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" #include "sandbox/linux/seccomp-bpf/trap.h" -namespace playground2 { +namespace sandbox { struct arch_seccomp_data; @@ -27,7 +27,7 @@ class ErrorCode { // completely arbitrary. But we want to pick it so that is is unlikely // to be passed in accidentally, when the user intended to return an // "errno" (see below) value instead. - ERR_ALLOWED = 0x04000000, + ERR_ALLOWED = 0x04000000, // Deny the system call with a particular "errno" value. // N.B.: It is also possible to return "0" here. That would normally @@ -46,7 +46,7 @@ class ErrorCode { // access 64bit quantities. But policies are still advised to specify whether // a system call expects a 32bit or a 64bit quantity. enum ArgType { - // When passed as an argument to Sandbox::Cond(), TP_32BIT requests that + // When passed as an argument to SandboxBPF::Cond(), TP_32BIT requests that // the conditional test should operate on the 32bit part of the system call // argument. // On 64bit architectures, this verifies that user space did not pass @@ -64,7 +64,7 @@ class ErrorCode { // having been sign extended. TP_32BIT, - // When passed as an argument to Sandbox::Cond(), TP_64BIT requests that + // When passed as an argument to SandboxBPF::Cond(), TP_64BIT requests that // the conditional test should operate on the full 64bit argument. It is // generally harmless to perform a 64bit test on 32bit systems, as the // kernel will always see the top 32 bits of all arguments as zero'd out. @@ -85,21 +85,26 @@ class ErrorCode { // need. // TODO(markus): Check whether we should automatically emulate signed // operations. - OP_GREATER_UNSIGNED, OP_GREATER_EQUAL_UNSIGNED, + OP_GREATER_UNSIGNED, + OP_GREATER_EQUAL_UNSIGNED, // Tests a system call argument against a bit mask. // The "ALL_BITS" variant performs this test: "arg & mask == mask" // This implies that a mask of zero always results in a passing test. // The "ANY_BITS" variant performs this test: "arg & mask != 0" // This implies that a mask of zero always results in a failing test. - OP_HAS_ALL_BITS, OP_HAS_ANY_BITS, + OP_HAS_ALL_BITS, + OP_HAS_ANY_BITS, // Total number of operations. OP_NUM_OPS, }; enum ErrorType { - ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND, + ET_INVALID, + ET_SIMPLE, + ET_TRAP, + ET_COND, }; // We allow the default constructor, as it makes the ErrorCode class @@ -107,10 +112,7 @@ class ErrorCode { // when compiling a BPF filter, we deliberately generate an invalid // program that will get flagged both by our Verifier class and by // the Linux kernel. - ErrorCode() : - error_type_(ET_INVALID), - err_(SECCOMP_RET_INVALID) { - } + ErrorCode() : error_type_(ET_INVALID), err_(SECCOMP_RET_INVALID) {} explicit ErrorCode(int err); // For all practical purposes, ErrorCodes are treated as if they were @@ -121,7 +123,7 @@ class ErrorCode { // callers handle life-cycle management for these objects. // Destructor - ~ErrorCode() { } + ~ErrorCode() {} bool Equals(const ErrorCode& err) const; bool LessThan(const ErrorCode& err) const; @@ -135,8 +137,8 @@ class ErrorCode { int argno() const { return argno_; } ArgType width() const { return width_; } Operation op() const { return op_; } - const ErrorCode *passed() const { return passed_; } - const ErrorCode *failed() const { return failed_; } + const ErrorCode* passed() const { return passed_; } + const ErrorCode* failed() const { return failed_; } struct LessThan { bool operator()(const ErrorCode& a, const ErrorCode& b) const { @@ -146,37 +148,41 @@ class ErrorCode { private: friend class CodeGen; - friend class Sandbox; + friend class SandboxBPF; friend class Trap; // If we are wrapping a callback, we must assign a unique id. This id is // how the kernel tells us which one of our different SECCOMP_RET_TRAP // cases has been triggered. - ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe, uint16_t id); + ErrorCode(Trap::TrapFnc fnc, const void* aux, bool safe, uint16_t id); // Some system calls require inspection of arguments. This constructor // allows us to specify additional constraints. - ErrorCode(int argno, ArgType width, Operation op, uint64_t value, - const ErrorCode *passed, const ErrorCode *failed); + ErrorCode(int argno, + ArgType width, + Operation op, + uint64_t value, + const ErrorCode* passed, + const ErrorCode* failed); ErrorType error_type_; union { // Fields needed for SECCOMP_RET_TRAP callbacks struct { - Trap::TrapFnc fnc_; // Callback function and arg, if trap was - void *aux_; // triggered by the kernel's BPF filter. - bool safe_; // Keep sandbox active while calling fnc_() + Trap::TrapFnc fnc_; // Callback function and arg, if trap was + void* aux_; // triggered by the kernel's BPF filter. + bool safe_; // Keep sandbox active while calling fnc_() }; // Fields needed when inspecting additional arguments. struct { - uint64_t value_; // Value that we are comparing with. - int argno_; // Syscall arg number that we are inspecting. - ArgType width_; // Whether we are looking at a 32/64bit value. + uint64_t value_; // Value that we are comparing with. + int argno_; // Syscall arg number that we are inspecting. + ArgType width_; // Whether we are looking at a 32/64bit value. Operation op_; // Comparison operation. - const ErrorCode *passed_; // Value to be returned if comparison passed, - const ErrorCode *failed_; // or if it failed. + const ErrorCode* passed_; // Value to be returned if comparison passed, + const ErrorCode* failed_; // or if it failed. }; }; @@ -184,9 +190,8 @@ class ErrorCode { // the value that uniquely identifies any ErrorCode and it (typically) can // be emitted directly into a BPF filter program. uint32_t err_; - }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc index 10f21324593..ef04a5f61f8 100644 --- a/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/tests/unit_tests.h" -using namespace playground2; +namespace sandbox { namespace { @@ -19,13 +21,13 @@ SANDBOX_TEST(ErrorCode, ErrnoConstructor) { ErrorCode e2(EPERM); SANDBOX_ASSERT(e2.err() == SECCOMP_RET_ERRNO + EPERM); - Sandbox sandbox; + SandboxBPF sandbox; ErrorCode e3 = sandbox.Trap(NULL, NULL); SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP); } SANDBOX_TEST(ErrorCode, Trap) { - Sandbox sandbox; + SandboxBPF sandbox; ErrorCode e0 = sandbox.Trap(NULL, "a"); ErrorCode e1 = sandbox.Trap(NULL, "b"); SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 == @@ -46,7 +48,7 @@ SANDBOX_TEST(ErrorCode, Equals) { ErrorCode e3(EPERM); SANDBOX_ASSERT(!e1.Equals(e3)); - Sandbox sandbox; + SandboxBPF sandbox; ErrorCode e4 = sandbox.Trap(NULL, "a"); ErrorCode e5 = sandbox.Trap(NULL, "b"); ErrorCode e6 = sandbox.Trap(NULL, "a"); @@ -67,7 +69,7 @@ SANDBOX_TEST(ErrorCode, LessThan) { SANDBOX_ASSERT(!e1.LessThan(e3)); SANDBOX_ASSERT( e3.LessThan(e1)); - Sandbox sandbox; + SandboxBPF sandbox; ErrorCode e4 = sandbox.Trap(NULL, "a"); ErrorCode e5 = sandbox.Trap(NULL, "b"); ErrorCode e6 = sandbox.Trap(NULL, "a"); @@ -79,3 +81,5 @@ SANDBOX_TEST(ErrorCode, LessThan) { } } // namespace + +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/instruction.h b/chromium/sandbox/linux/seccomp-bpf/instruction.h index 0fc8123280e..8567c8fd91c 100644 --- a/chromium/sandbox/linux/seccomp-bpf/instruction.h +++ b/chromium/sandbox/linux/seccomp-bpf/instruction.h @@ -7,8 +7,7 @@ #include - -namespace playground2 { +namespace sandbox { // The fields in this structure have the same meaning as the corresponding // fields in "struct sock_filter". See for a lot more @@ -27,12 +26,12 @@ namespace playground2 { struct Instruction { // Constructor for an non-jumping instruction or for an unconditional // "always" jump. - Instruction(uint16_t c, uint32_t parm, Instruction *n) : - code(c), next(n), k(parm) { } + Instruction(uint16_t c, uint32_t parm, Instruction* n) + : code(c), next(n), k(parm) {} // Constructor for a conditional jump instruction. - Instruction(uint16_t c, uint32_t parm, Instruction *jt, Instruction *jf) : - code(c), jt_ptr(jt), jf_ptr(jf), k(parm) { } + Instruction(uint16_t c, uint32_t parm, Instruction* jt, Instruction* jf) + : code(c), jt_ptr(jt), jf_ptr(jf), k(parm) {} uint16_t code; union { @@ -47,17 +46,17 @@ struct Instruction { // keys in a TargetsToBlocks map and should no longer be dereferenced // directly. struct { - Instruction *jt_ptr, *jf_ptr; + Instruction* jt_ptr, *jf_ptr; }; // While assembling the BPF program, non-jumping instructions are linked // by the "next_" pointer. This field is no longer needed when we have // computed basic blocks. - Instruction *next; + Instruction* next; }; uint32_t k; }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/port.h b/chromium/sandbox/linux/seccomp-bpf/port.h deleted file mode 100644 index f10b1481de5..00000000000 --- a/chromium/sandbox/linux/seccomp-bpf/port.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 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. - -// Commonly used macro definitions to make the code build in different -// target environments (e.g. as part of Chrome vs. stand-alone) - -#ifndef SANDBOX_LINUX_SECCOMP_BPF_PORT_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_PORT_H__ - -#if !defined(SECCOMP_BPF_STANDALONE) - #include "base/basictypes.h" - #include "base/logging.h" - #include "base/posix/eintr_wrapper.h" -#else - #define arraysize(x) (sizeof(x)/sizeof(*(x))) - - #define HANDLE_EINTR TEMP_FAILURE_RETRY - - #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - - #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ - DISALLOW_COPY_AND_ASSIGN(TypeName) - - template - struct CompileAssert { - }; - - #define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] -#endif - -#endif // SANDBOX_LINUX_SECCOMP_BPF_PORT_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index 1dc5eae0428..6b2327e5452 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" + // Some headers on Android are missing cdefs: crbug.com/172337. // (We can't use OS_ANDROID here since build_config.h is not included). #if defined(ANDROID) @@ -18,63 +20,54 @@ #include #include -#ifndef SECCOMP_BPF_STANDALONE +#include "base/compiler_specific.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" -#endif - #include "sandbox/linux/seccomp-bpf/codegen.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" #include "sandbox/linux/seccomp-bpf/syscall.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/seccomp-bpf/verifier.h" -namespace { +namespace sandbox { -using playground2::ErrorCode; -using playground2::Instruction; -using playground2::Sandbox; -using playground2::Trap; -using playground2::arch_seccomp_data; +namespace { const int kExpectedExitCode = 100; -template int popcount(T x); -template<> int popcount(unsigned int x) { +int popcount(uint32_t x) { return __builtin_popcount(x); } -template<> int popcount(unsigned long x) { - return __builtin_popcountl(x); -} -template<> int popcount(unsigned long long x) { - return __builtin_popcountll(x); -} +#if !defined(NDEBUG) void WriteFailedStderrSetupMessage(int out_fd) { const char* error_string = strerror(errno); - static const char msg[] = "You have reproduced a puzzling issue.\n" - "Please, report to crbug.com/152530!\n" - "Failed to set up stderr: "; - if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string && + static const char msg[] = + "You have reproduced a puzzling issue.\n" + "Please, report to crbug.com/152530!\n" + "Failed to set up stderr: "; + if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string && HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 && HANDLE_EINTR(write(out_fd, "\n", 1))) { } } +#endif // !defined(NDEBUG) // We define a really simple sandbox policy. It is just good enough for us // to tell that the sandbox has actually been activated. -ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) __attribute__((const)); -ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) { +ErrorCode ProbeEvaluator(SandboxBPF*, int sysnum, void*) __attribute__((const)); +ErrorCode ProbeEvaluator(SandboxBPF*, int sysnum, void*) { switch (sysnum) { - case __NR_getpid: - // Return EPERM so that we can check that the filter actually ran. - return ErrorCode(EPERM); - case __NR_exit_group: - // Allow exit() with a non-default return code. - return ErrorCode(ErrorCode::ERR_ALLOWED); - default: - // Make everything else fail in an easily recognizable way. - return ErrorCode(EINVAL); + case __NR_getpid: + // Return EPERM so that we can check that the filter actually ran. + return ErrorCode(EPERM); + case __NR_exit_group: + // Allow exit() with a non-default return code. + return ErrorCode(ErrorCode::ERR_ALLOWED); + default: + // Make everything else fail in an easily recognizable way. + return ErrorCode(EINVAL); } } @@ -84,8 +77,8 @@ void ProbeProcess(void) { } } -ErrorCode AllowAllEvaluator(Sandbox *, int sysnum, void *) { - if (!Sandbox::IsValidSyscallNumber(sysnum)) { +ErrorCode AllowAllEvaluator(SandboxBPF*, int sysnum, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysnum)) { return ErrorCode(ENOSYS); } return ErrorCode(ErrorCode::ERR_ALLOWED); @@ -110,12 +103,11 @@ bool IsSingleThreaded(int proc_fd) { struct stat sb; int task = -1; - if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 || - fstat(task, &sb) != 0 || - sb.st_nlink != 3 || - HANDLE_EINTR(close(task))) { + if ((task = openat(proc_fd, "self/task", O_RDONLY | O_DIRECTORY)) < 0 || + fstat(task, &sb) != 0 || sb.st_nlink != 3 || IGNORE_EINTR(close(task))) { if (task >= 0) { - if (HANDLE_EINTR(close(task))) { } + if (IGNORE_EINTR(close(task))) { + } } return false; } @@ -131,14 +123,13 @@ bool IsDenied(const ErrorCode& code) { // Function that can be passed as a callback function to CodeGen::Traverse(). // Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it // sets the "bool" variable pointed to by "aux". -void CheckForUnsafeErrorCodes(Instruction *insn, void *aux) { - bool *is_unsafe = static_cast(aux); +void CheckForUnsafeErrorCodes(Instruction* insn, void* aux) { + bool* is_unsafe = static_cast(aux); if (!*is_unsafe) { - if (BPF_CLASS(insn->code) == BPF_RET && - insn->k > SECCOMP_RET_TRAP && + if (BPF_CLASS(insn->code) == BPF_RET && insn->k > SECCOMP_RET_TRAP && insn->k - SECCOMP_RET_TRAP <= SECCOMP_RET_DATA) { const ErrorCode& err = - Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA); + Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA); if (err.error_type() != ErrorCode::ET_INVALID && !err.safe()) { *is_unsafe = true; } @@ -148,7 +139,7 @@ void CheckForUnsafeErrorCodes(Instruction *insn, void *aux) { // A Trap() handler that returns an "errno" value. The value is encoded // in the "aux" parameter. -intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux) { +intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) { // TrapFnc functions report error by following the native kernel convention // of returning an exit code in the range of -1..-4096. They do not try to // set errno themselves. The glibc wrapper that triggered the SIGSYS will @@ -161,7 +152,7 @@ intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux) { // Checks whether the "insn" returns an errno value from a BPF filter. If so, // it rewrites the instruction to instead call a Trap() handler that does // the same thing. "aux" is ignored. -void RedirectToUserspace(Instruction *insn, void *aux) { +void RedirectToUserspace(Instruction* insn, void* aux) { // When inside an UnsafeTrap() callback, we want to allow all system calls. // This means, we must conditionally disable the sandbox -- and that's not // something that kernel-side BPF filters can do, as they cannot inspect @@ -171,54 +162,74 @@ void RedirectToUserspace(Instruction *insn, void *aux) { // The performance penalty for this extra round-trip to user-space is not // actually that bad, as we only ever pay it for denied system calls; and a // typical program has very few of these. - Sandbox *sandbox = static_cast(aux); + SandboxBPF* sandbox = static_cast(aux); if (BPF_CLASS(insn->code) == BPF_RET && (insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { insn->k = sandbox->Trap(ReturnErrno, - reinterpret_cast(insn->k & SECCOMP_RET_DATA)).err(); + reinterpret_cast(insn->k & SECCOMP_RET_DATA)).err(); } } -// Stackable wrapper around an Evaluators handler. Changes ErrorCodes -// returned by a system call evaluator to match the changes made by -// RedirectToUserspace(). "aux" should be pointer to wrapped system call -// evaluator. -ErrorCode RedirectToUserspaceEvalWrapper(Sandbox *sandbox, int sysnum, - void *aux) { - // We need to replicate the behavior of RedirectToUserspace(), so that our - // Verifier can still work correctly. - Sandbox::Evaluators *evaluators = - reinterpret_cast(aux); - const std::pair& evaluator = - *evaluators->begin(); +// This wraps an existing policy and changes its behavior to match the changes +// made by RedirectToUserspace(). This is part of the framework that allows BPF +// evaluation in userland. +// TODO(markus): document the code inside better. +class RedirectToUserSpacePolicyWrapper : public SandboxBPFPolicy { + public: + explicit RedirectToUserSpacePolicyWrapper( + const SandboxBPFPolicy* wrapped_policy) + : wrapped_policy_(wrapped_policy) { + DCHECK(wrapped_policy_); + } - ErrorCode err = evaluator.first(sandbox, sysnum, evaluator.second); - if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { - return sandbox->Trap(ReturnErrno, - reinterpret_cast(err.err() & SECCOMP_RET_DATA)); + virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, + int system_call_number) const OVERRIDE { + ErrorCode err = + wrapped_policy_->EvaluateSyscall(sandbox_compiler, system_call_number); + if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { + return sandbox_compiler->Trap( + ReturnErrno, reinterpret_cast(err.err() & SECCOMP_RET_DATA)); + } + return err; } - return err; -} -intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) { - SANDBOX_DIE(static_cast(aux)); + private: + const SandboxBPFPolicy* wrapped_policy_; + DISALLOW_COPY_AND_ASSIGN(RedirectToUserSpacePolicyWrapper); +}; + +intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) { + SANDBOX_DIE(static_cast(aux)); } -} // namespace +// This class allows compatibility with the old, deprecated SetSandboxPolicy. +class CompatibilityPolicy : public SandboxBPFPolicy { + public: + CompatibilityPolicy(SandboxBPF::EvaluateSyscall syscall_evaluator, void* aux) + : syscall_evaluator_(syscall_evaluator), aux_(aux) { + DCHECK(syscall_evaluator_); + } + + virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, + int system_call_number) const OVERRIDE { + return syscall_evaluator_(sandbox_compiler, system_call_number, aux_); + } -// The kernel gives us a sandbox, we turn it into a playground :-) -// This is version 2 of the playground; version 1 was built on top of -// pre-BPF seccomp mode. -namespace playground2 { + private: + SandboxBPF::EvaluateSyscall syscall_evaluator_; + void* aux_; + DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy); +}; -Sandbox::Sandbox() +} // namespace + +SandboxBPF::SandboxBPF() : quiet_(false), proc_fd_(-1), - evaluators_(new Evaluators), - conds_(new Conds) { -} + conds_(new Conds), + sandbox_has_started_(false) {} -Sandbox::~Sandbox() { +SandboxBPF::~SandboxBPF() { // It is generally unsafe to call any memory allocator operations or to even // call arbitrary destructors after having installed a new policy. We just // have no way to tell whether this policy would allow the system calls that @@ -230,31 +241,26 @@ Sandbox::~Sandbox() { // The "if ()" statements are technically superfluous. But let's be explicit // that we really don't want to run any code, when we already destroyed // objects before setting up the sandbox. - if (evaluators_) { - delete evaluators_; - } if (conds_) { delete conds_; } } -bool Sandbox::IsValidSyscallNumber(int sysnum) { +bool SandboxBPF::IsValidSyscallNumber(int sysnum) { return SyscallIterator::IsValid(sysnum); } - -bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), - Sandbox::EvaluateSyscall syscall_evaluator, - void *aux) { +bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), + EvaluateSyscall syscall_evaluator, + void* aux) { // Block all signals before forking a child process. This prevents an // attacker from manipulating our test by sending us an unexpected signal. sigset_t old_mask, new_mask; - if (sigfillset(&new_mask) || - sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) { + if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) { SANDBOX_DIE("sigprocmask() failed"); } int fds[2]; - if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) { + if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) { SANDBOX_DIE("pipe() failed"); } @@ -262,6 +268,10 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), SANDBOX_DIE("Process started without standard file descriptors"); } + // This code is using fork() and should only ever run single-threaded. + // Most of the code below is "async-signal-safe" and only minor changes + // would be needed to support threads. + DCHECK(IsSingleThreaded(proc_fd_)); pid_t pid = fork(); if (pid < 0) { // Die if we cannot fork(). We would probably fail a little later @@ -281,7 +291,7 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), Die::EnableSimpleExit(); errno = 0; - if (HANDLE_EINTR(close(fds[0]))) { + if (IGNORE_EINTR(close(fds[0]))) { // This call to close() has been failing in strange ways. See // crbug.com/152530. So we only fail in debug mode now. #if !defined(NDEBUG) @@ -303,7 +313,7 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), SANDBOX_DIE(NULL); #endif } - if (HANDLE_EINTR(close(fds[1]))) { + if (IGNORE_EINTR(close(fds[1]))) { // This call to close() has been failing in strange ways. See // crbug.com/152530. So we only fail in debug mode now. #if !defined(NDEBUG) @@ -312,7 +322,7 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), #endif } - SetSandboxPolicy(syscall_evaluator, aux); + SetSandboxPolicyDeprecated(syscall_evaluator, aux); StartSandbox(); // Run our code in the sandbox. @@ -323,7 +333,7 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), } // In the parent process. - if (HANDLE_EINTR(close(fds[1]))) { + if (IGNORE_EINTR(close(fds[1]))) { SANDBOX_DIE("close() failed"); } if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) { @@ -344,27 +354,26 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), char buf[4096]; ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); if (len > 0) { - while (len > 1 && buf[len-1] == '\n') { + while (len > 1 && buf[len - 1] == '\n') { --len; } buf[len] = '\000'; SANDBOX_DIE(buf); } } - if (HANDLE_EINTR(close(fds[0]))) { + if (IGNORE_EINTR(close(fds[0]))) { SANDBOX_DIE("close() failed"); } return rc; } -bool Sandbox::KernelSupportSeccompBPF() { - return - RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) && - RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0); +bool SandboxBPF::KernelSupportSeccompBPF() { + return RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) && + RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0); } -Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) { +SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) { // It the sandbox is currently active, we clearly must have support for // sandboxing. if (status_ == STATUS_ENABLED) { @@ -399,14 +408,14 @@ Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) { // We create our own private copy of a "Sandbox" object. This ensures that // the object does not have any policies configured, that might interfere // with the tests done by "KernelSupportSeccompBPF()". - Sandbox sandbox; + SandboxBPF sandbox; // By setting "quiet_ = true" we suppress messages for expected and benign // failures (e.g. if the current kernel lacks support for BPF filters). sandbox.quiet_ = true; sandbox.set_proc_fd(proc_fd); - status_ = sandbox.KernelSupportSeccompBPF() - ? STATUS_AVAILABLE : STATUS_UNSUPPORTED; + status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE + : STATUS_UNSUPPORTED; // As we are performing our tests from a child process, the run-time // environment that is visible to the sandbox is always guaranteed to be @@ -419,20 +428,20 @@ Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) { return status_; } -void Sandbox::set_proc_fd(int proc_fd) { - proc_fd_ = proc_fd; -} +void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; } -void Sandbox::StartSandbox() { +void SandboxBPF::StartSandbox() { if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { - SANDBOX_DIE("Trying to start sandbox, even though it is known to be " - "unavailable"); - } else if (!evaluators_ || !conds_) { - SANDBOX_DIE("Cannot repeatedly start sandbox. Create a separate Sandbox " - "object instead."); + SANDBOX_DIE( + "Trying to start sandbox, even though it is known to be " + "unavailable"); + } else if (sandbox_has_started_ || !conds_) { + SANDBOX_DIE( + "Cannot repeatedly start sandbox. Create a separate Sandbox " + "object instead."); } if (proc_fd_ < 0) { - proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY); + proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY); } if (proc_fd_ < 0) { // For now, continue in degraded mode, if we can't access /proc. @@ -446,7 +455,7 @@ void Sandbox::StartSandbox() { // before installing the filters, just in case that our policy denies // close(). if (proc_fd_ >= 0) { - if (HANDLE_EINTR(close(proc_fd_))) { + if (IGNORE_EINTR(close(proc_fd_))) { SANDBOX_DIE("Failed to close file descriptor for /proc"); } proc_fd_ = -1; @@ -459,27 +468,38 @@ void Sandbox::StartSandbox() { status_ = STATUS_ENABLED; } -void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator, - void *aux) { - for (SyscallIterator iter(true); !iter.Done(); ) { +void SandboxBPF::PolicySanityChecks(SandboxBPFPolicy* policy) { + for (SyscallIterator iter(true); !iter.Done();) { uint32_t sysnum = iter.Next(); - if (!IsDenied(syscall_evaluator(this, sysnum, aux))) { - SANDBOX_DIE("Policies should deny system calls that are outside the " - "expected range (typically MIN_SYSCALL..MAX_SYSCALL)"); + if (!IsDenied(policy->EvaluateSyscall(this, sysnum))) { + SANDBOX_DIE( + "Policies should deny system calls that are outside the " + "expected range (typically MIN_SYSCALL..MAX_SYSCALL)"); } } return; } -void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) { - if (!evaluators_ || !conds_) { +// Deprecated API, supported with a wrapper to the new API. +void SandboxBPF::SetSandboxPolicyDeprecated(EvaluateSyscall syscall_evaluator, + void* aux) { + if (sandbox_has_started_ || !conds_) { SANDBOX_DIE("Cannot change policy after sandbox has started"); } - PolicySanityChecks(syscall_evaluator, aux); - evaluators_->push_back(std::make_pair(syscall_evaluator, aux)); + SetSandboxPolicy(new CompatibilityPolicy(syscall_evaluator, aux)); } -void Sandbox::InstallFilter() { +// Don't take a scoped_ptr here, polymorphism make their use awkward. +void SandboxBPF::SetSandboxPolicy(SandboxBPFPolicy* policy) { + DCHECK(!policy_); + if (sandbox_has_started_ || !conds_) { + SANDBOX_DIE("Cannot change policy after sandbox has started"); + } + PolicySanityChecks(policy); + policy_.reset(policy); +} + +void SandboxBPF::InstallFilter() { // We want to be very careful in not imposing any requirements on the // policies that are set with SetSandboxPolicy(). This means, as soon as // the sandbox is active, we shouldn't be relying on libraries that could @@ -491,19 +511,20 @@ void Sandbox::InstallFilter() { // installed the BPF filter program in the kernel. Depending on the // system memory allocator that is in effect, these operators can result // in system calls to things like munmap() or brk(). - Program *program = AssembleFilter(false /* force_verification */); + Program* program = AssembleFilter(false /* force_verification */); struct sock_filter bpf[program->size()]; - const struct sock_fprog prog = { - static_cast(program->size()), bpf }; + const struct sock_fprog prog = {static_cast(program->size()), + bpf}; memcpy(bpf, &(*program)[0], sizeof(bpf)); delete program; - // Release memory that is no longer needed - delete evaluators_; + // Make an attempt to release memory that is no longer needed here, rather + // than in the destructor. Try to avoid as much as possible to presume of + // what will be possible to do in the new (sandboxed) execution environment. delete conds_; - evaluators_ = NULL; - conds_ = NULL; + conds_ = NULL; + policy_.reset(); // Install BPF filter program if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { @@ -514,41 +535,38 @@ void Sandbox::InstallFilter() { } } + sandbox_has_started_ = true; + return; } -Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { +SandboxBPF::Program* SandboxBPF::AssembleFilter(bool force_verification) { #if !defined(NDEBUG) force_verification = true; #endif // Verify that the user pushed a policy. - if (evaluators_->empty()) { - SANDBOX_DIE("Failed to configure system call filters"); - } - - // We can't handle stacked evaluators, yet. We'll get there eventually - // though. Hang tight. - if (evaluators_->size() != 1) { - SANDBOX_DIE("Not implemented"); - } + DCHECK(policy_); // Assemble the BPF filter program. - CodeGen *gen = new CodeGen(); + CodeGen* gen = new CodeGen(); if (!gen) { SANDBOX_DIE("Out of memory"); } // If the architecture doesn't match SECCOMP_ARCH, disallow the // system call. - Instruction *tail; - Instruction *head = - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_ARCH_IDX, - tail = - gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH, - NULL, - gen->MakeInstruction(BPF_RET+BPF_K, - Kill("Invalid audit architecture in BPF filter")))); + Instruction* tail; + Instruction* head = gen->MakeInstruction( + BPF_LD + BPF_W + BPF_ABS, + SECCOMP_ARCH_IDX, + tail = gen->MakeInstruction( + BPF_JMP + BPF_JEQ + BPF_K, + SECCOMP_ARCH, + NULL, + gen->MakeInstruction( + BPF_RET + BPF_K, + Kill("Invalid audit architecture in BPF filter")))); bool has_unsafe_traps = false; { @@ -558,8 +576,8 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { FindRanges(&ranges); // Compile the system call ranges to an optimized BPF jumptable - Instruction *jumptable = - AssembleJumpTable(gen, ranges.begin(), ranges.end()); + Instruction* jumptable = + AssembleJumpTable(gen, ranges.begin(), ranges.end()); // If there is at least one UnsafeTrap() in our program, the entire sandbox // is unsafe. We need to modify the program so that all non- @@ -569,8 +587,8 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { gen->Traverse(jumptable, CheckForUnsafeErrorCodes, &has_unsafe_traps); // Grab the system call number, so that we can implement jump tables. - Instruction *load_nr = - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_NR_IDX); + Instruction* load_nr = + gen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX); // If our BPF program has unsafe jumps, enable support for them. This // test happens very early in the BPF filter program. Even before we @@ -581,27 +599,29 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { // is actually requested by the sandbox policy. if (has_unsafe_traps) { if (SandboxSyscall(-1) == -1 && errno == ENOSYS) { - SANDBOX_DIE("Support for UnsafeTrap() has not yet been ported to this " - "architecture"); + SANDBOX_DIE( + "Support for UnsafeTrap() has not yet been ported to this " + "architecture"); } - EvaluateSyscall evaluateSyscall = evaluators_->begin()->first; - void *aux = evaluators_->begin()->second; - if (!evaluateSyscall(this, __NR_rt_sigprocmask, aux). - Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) || - !evaluateSyscall(this, __NR_rt_sigreturn, aux). - Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) + if (!policy_->EvaluateSyscall(this, __NR_rt_sigprocmask) + .Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) || + !policy_->EvaluateSyscall(this, __NR_rt_sigreturn) + .Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) #if defined(__NR_sigprocmask) - || !evaluateSyscall(this, __NR_sigprocmask, aux). - Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) + || + !policy_->EvaluateSyscall(this, __NR_sigprocmask) + .Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) #endif #if defined(__NR_sigreturn) - || !evaluateSyscall(this, __NR_sigreturn, aux). - Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) + || + !policy_->EvaluateSyscall(this, __NR_sigreturn) + .Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) #endif ) { - SANDBOX_DIE("Invalid seccomp policy; if using UnsafeTrap(), you must " - "unconditionally allow sigreturn() and sigprocmask()"); + SANDBOX_DIE( + "Invalid seccomp policy; if using UnsafeTrap(), you must " + "unconditionally allow sigreturn() and sigprocmask()"); } if (!Trap::EnableUnsafeTrapsInSigSysHandler()) { @@ -617,49 +637,58 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { // Allow system calls, if they originate from our magic return address // (which we can query by calling SandboxSyscall(-1)). uintptr_t syscall_entry_point = - static_cast(SandboxSyscall(-1)); + static_cast(SandboxSyscall(-1)); uint32_t low = static_cast(syscall_entry_point); #if __SIZEOF_POINTER__ > 4 - uint32_t hi = static_cast(syscall_entry_point >> 32); + uint32_t hi = static_cast(syscall_entry_point >> 32); #endif // BPF cannot do native 64bit comparisons. On 64bit architectures, we // have to compare both 32bit halves of the instruction pointer. If they // match what we expect, we return ERR_ALLOWED. If either or both don't // match, we continue evalutating the rest of the sandbox policy. - Instruction *escape_hatch = - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_LSB_IDX, - gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, low, + Instruction* escape_hatch = gen->MakeInstruction( + BPF_LD + BPF_W + BPF_ABS, + SECCOMP_IP_LSB_IDX, + gen->MakeInstruction( + BPF_JMP + BPF_JEQ + BPF_K, + low, #if __SIZEOF_POINTER__ > 4 - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_MSB_IDX, - gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, hi, + gen->MakeInstruction( + BPF_LD + BPF_W + BPF_ABS, + SECCOMP_IP_MSB_IDX, + gen->MakeInstruction( + BPF_JMP + BPF_JEQ + BPF_K, + hi, #endif - gen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED)), + gen->MakeInstruction(BPF_RET + BPF_K, + ErrorCode(ErrorCode::ERR_ALLOWED)), #if __SIZEOF_POINTER__ > 4 - load_nr)), + load_nr)), #endif - load_nr)); + load_nr)); gen->JoinInstructions(tail, escape_hatch); } else { gen->JoinInstructions(tail, load_nr); } tail = load_nr; - // On Intel architectures, verify that system call numbers are in the - // expected number range. The older i386 and x86-64 APIs clear bit 30 - // on all system calls. The newer x32 API always sets bit 30. +// On Intel architectures, verify that system call numbers are in the +// expected number range. The older i386 and x86-64 APIs clear bit 30 +// on all system calls. The newer x32 API always sets bit 30. #if defined(__i386__) || defined(__x86_64__) - Instruction *invalidX32 = - gen->MakeInstruction(BPF_RET+BPF_K, - Kill("Illegal mixing of system call ABIs").err_); - Instruction *checkX32 = + Instruction* invalidX32 = gen->MakeInstruction( + BPF_RET + BPF_K, Kill("Illegal mixing of system call ABIs").err_); + Instruction* checkX32 = #if defined(__x86_64__) && defined(__ILP32__) - gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, invalidX32); + gen->MakeInstruction( + BPF_JMP + BPF_JSET + BPF_K, 0x40000000, 0, invalidX32); #else - gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, invalidX32, 0); + gen->MakeInstruction( + BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, 0); #endif - gen->JoinInstructions(tail, checkX32); - tail = checkX32; + gen->JoinInstructions(tail, checkX32); + tail = checkX32; #endif // Append jump table to our pre-amble @@ -667,7 +696,7 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { } // Turn the DAG into a vector of instructions. - Program *program = new Program(); + Program* program = new Program(); gen->Compile(head, program); delete gen; @@ -684,41 +713,37 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { return program; } -void Sandbox::VerifyProgram(const Program& program, bool has_unsafe_traps) { +void SandboxBPF::VerifyProgram(const Program& program, bool has_unsafe_traps) { // If we previously rewrote the BPF program so that it calls user-space // whenever we return an "errno" value from the filter, then we have to // wrap our system call evaluator to perform the same operation. Otherwise, // the verifier would also report a mismatch in return codes. - Evaluators redirected_evaluators; - redirected_evaluators.push_back( - std::make_pair(RedirectToUserspaceEvalWrapper, evaluators_)); - - const char *err = NULL; - if (!Verifier::VerifyBPF( - this, - program, - has_unsafe_traps ? redirected_evaluators : *evaluators_, - &err)) { + scoped_ptr redirected_policy( + new RedirectToUserSpacePolicyWrapper(policy_.get())); + + const char* err = NULL; + if (!Verifier::VerifyBPF(this, + program, + has_unsafe_traps ? *redirected_policy : *policy_, + &err)) { CodeGen::PrintProgram(program); SANDBOX_DIE(err); } } -void Sandbox::FindRanges(Ranges *ranges) { +void SandboxBPF::FindRanges(Ranges* ranges) { // Please note that "struct seccomp_data" defines system calls as a signed // int32_t, but BPF instructions always operate on unsigned quantities. We // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, // and then verifying that the rest of the number range (both positive and // negative) all return the same ErrorCode. - EvaluateSyscall evaluate_syscall = evaluators_->begin()->first; - void *aux = evaluators_->begin()->second; - uint32_t old_sysnum = 0; - ErrorCode old_err = evaluate_syscall(this, old_sysnum, aux); - ErrorCode invalid_err = evaluate_syscall(this, MIN_SYSCALL - 1, - aux); - for (SyscallIterator iter(false); !iter.Done(); ) { + uint32_t old_sysnum = 0; + ErrorCode old_err = policy_->EvaluateSyscall(this, old_sysnum); + ErrorCode invalid_err = policy_->EvaluateSyscall(this, MIN_SYSCALL - 1); + + for (SyscallIterator iter(false); !iter.Done();) { uint32_t sysnum = iter.Next(); - ErrorCode err = evaluate_syscall(this, static_cast(sysnum), aux); + ErrorCode err = policy_->EvaluateSyscall(this, static_cast(sysnum)); if (!iter.IsValid(sysnum) && !invalid_err.Equals(err)) { // A proper sandbox policy should always treat system calls outside of // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns @@ -729,14 +754,14 @@ void Sandbox::FindRanges(Ranges *ranges) { if (!err.Equals(old_err) || iter.Done()) { ranges->push_back(Range(old_sysnum, sysnum - 1, old_err)); old_sysnum = sysnum; - old_err = err; + old_err = err; } } } -Instruction *Sandbox::AssembleJumpTable(CodeGen *gen, - Ranges::const_iterator start, - Ranges::const_iterator stop) { +Instruction* SandboxBPF::AssembleJumpTable(CodeGen* gen, + Ranges::const_iterator start, + Ranges::const_iterator stop) { // We convert the list of system call ranges into jump table that performs // a binary search over the ranges. // As a sanity check, we need to have at least one distinct ranges for us @@ -753,166 +778,170 @@ Instruction *Sandbox::AssembleJumpTable(CodeGen *gen, // We compare our system call number against the lowest valid system call // number in this range object. If our number is lower, it is outside of // this range object. If it is greater or equal, it might be inside. - Ranges::const_iterator mid = start + (stop - start)/2; + Ranges::const_iterator mid = start + (stop - start) / 2; // Sub-divide the list of ranges and continue recursively. - Instruction *jf = AssembleJumpTable(gen, start, mid); - Instruction *jt = AssembleJumpTable(gen, mid, stop); - return gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, mid->from, jt, jf); + Instruction* jf = AssembleJumpTable(gen, start, mid); + Instruction* jt = AssembleJumpTable(gen, mid, stop); + return gen->MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); } -Instruction *Sandbox::RetExpression(CodeGen *gen, const ErrorCode& err) { +Instruction* SandboxBPF::RetExpression(CodeGen* gen, const ErrorCode& err) { if (err.error_type_ == ErrorCode::ET_COND) { return CondExpression(gen, err); } else { - return gen->MakeInstruction(BPF_RET+BPF_K, err); + return gen->MakeInstruction(BPF_RET + BPF_K, err); } } -Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) { +Instruction* SandboxBPF::CondExpression(CodeGen* gen, const ErrorCode& cond) { // We can only inspect the six system call arguments that are passed in // CPU registers. if (cond.argno_ < 0 || cond.argno_ >= 6) { - SANDBOX_DIE("Internal compiler error; invalid argument number " - "encountered"); + SANDBOX_DIE( + "Internal compiler error; invalid argument number " + "encountered"); } // BPF programs operate on 32bit entities. Load both halfs of the 64bit // system call argument and then generate suitable conditional statements. - Instruction *msb_head = - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, - SECCOMP_ARG_MSB_IDX(cond.argno_)); - Instruction *msb_tail = msb_head; - Instruction *lsb_head = - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, - SECCOMP_ARG_LSB_IDX(cond.argno_)); - Instruction *lsb_tail = lsb_head; + Instruction* msb_head = gen->MakeInstruction( + BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARG_MSB_IDX(cond.argno_)); + Instruction* msb_tail = msb_head; + Instruction* lsb_head = gen->MakeInstruction( + BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARG_LSB_IDX(cond.argno_)); + Instruction* lsb_tail = lsb_head; // Emit a suitable comparison statement. switch (cond.op_) { - case ErrorCode::OP_EQUAL: - // Compare the least significant bits for equality - lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, - static_cast(cond.value_), - RetExpression(gen, *cond.passed_), - RetExpression(gen, *cond.failed_)); - gen->JoinInstructions(lsb_head, lsb_tail); - - // If we are looking at a 64bit argument, we need to also compare the - // most significant bits. - if (cond.width_ == ErrorCode::TP_64BIT) { - msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, - static_cast(cond.value_ >> 32), - lsb_head, + case ErrorCode::OP_EQUAL: + // Compare the least significant bits for equality + lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, + static_cast(cond.value_), + RetExpression(gen, *cond.passed_), RetExpression(gen, *cond.failed_)); - gen->JoinInstructions(msb_head, msb_tail); - } - break; - case ErrorCode::OP_HAS_ALL_BITS: - // Check the bits in the LSB half of the system call argument. Our - // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is - // different from the kernel's BPF_JSET operation which passes, if any of - // the bits are set. - // Of course, if there is only a single set bit (or none at all), then - // things get easier. - { - uint32_t lsb_bits = static_cast(cond.value_); - int lsb_bit_count = popcount(lsb_bits); - if (lsb_bit_count == 0) { - // No bits are set in the LSB half. The test will always pass. - lsb_head = RetExpression(gen, *cond.passed_); - lsb_tail = NULL; - } else if (lsb_bit_count == 1) { - // Exactly one bit is set in the LSB half. We can use the BPF_JSET - // operator. - lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, - lsb_bits, - RetExpression(gen, *cond.passed_), - RetExpression(gen, *cond.failed_)); - gen->JoinInstructions(lsb_head, lsb_tail); - } else { - // More than one bit is set in the LSB half. We need to combine - // BPF_AND and BPF_JEQ to test whether all of these bits are in fact - // set in the system call argument. - gen->JoinInstructions(lsb_head, - gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K, - lsb_bits, - lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, + gen->JoinInstructions(lsb_head, lsb_tail); + + // If we are looking at a 64bit argument, we need to also compare the + // most significant bits. + if (cond.width_ == ErrorCode::TP_64BIT) { + msb_tail = + gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, + static_cast(cond.value_ >> 32), + lsb_head, + RetExpression(gen, *cond.failed_)); + gen->JoinInstructions(msb_head, msb_tail); + } + break; + case ErrorCode::OP_HAS_ALL_BITS: + // Check the bits in the LSB half of the system call argument. Our + // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is + // different from the kernel's BPF_JSET operation which passes, if any of + // the bits are set. + // Of course, if there is only a single set bit (or none at all), then + // things get easier. + { + uint32_t lsb_bits = static_cast(cond.value_); + int lsb_bit_count = popcount(lsb_bits); + if (lsb_bit_count == 0) { + // No bits are set in the LSB half. The test will always pass. + lsb_head = RetExpression(gen, *cond.passed_); + lsb_tail = NULL; + } else if (lsb_bit_count == 1) { + // Exactly one bit is set in the LSB half. We can use the BPF_JSET + // operator. + lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, lsb_bits, RetExpression(gen, *cond.passed_), - RetExpression(gen, *cond.failed_)))); + RetExpression(gen, *cond.failed_)); + gen->JoinInstructions(lsb_head, lsb_tail); + } else { + // More than one bit is set in the LSB half. We need to combine + // BPF_AND and BPF_JEQ to test whether all of these bits are in fact + // set in the system call argument. + gen->JoinInstructions( + lsb_head, + gen->MakeInstruction(BPF_ALU + BPF_AND + BPF_K, + lsb_bits, + lsb_tail = gen->MakeInstruction( + BPF_JMP + BPF_JEQ + BPF_K, + lsb_bits, + RetExpression(gen, *cond.passed_), + RetExpression(gen, *cond.failed_)))); + } } - } - // If we are looking at a 64bit argument, we need to also check the bits - // in the MSB half of the system call argument. - if (cond.width_ == ErrorCode::TP_64BIT) { - uint32_t msb_bits = static_cast(cond.value_ >> 32); - int msb_bit_count = popcount(msb_bits); - if (msb_bit_count == 0) { - // No bits are set in the MSB half. The test will always pass. - msb_head = lsb_head; - } else if (msb_bit_count == 1) { - // Exactly one bit is set in the MSB half. We can use the BPF_JSET - // operator. - msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, - msb_bits, - lsb_head, - RetExpression(gen, *cond.failed_)); - gen->JoinInstructions(msb_head, msb_tail); - } else { - // More than one bit is set in the MSB half. We need to combine - // BPF_AND and BPF_JEQ to test whether all of these bits are in fact - // set in the system call argument. - gen->JoinInstructions(msb_head, - gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K, - msb_bits, - gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, - msb_bits, - lsb_head, - RetExpression(gen, *cond.failed_)))); + // If we are looking at a 64bit argument, we need to also check the bits + // in the MSB half of the system call argument. + if (cond.width_ == ErrorCode::TP_64BIT) { + uint32_t msb_bits = static_cast(cond.value_ >> 32); + int msb_bit_count = popcount(msb_bits); + if (msb_bit_count == 0) { + // No bits are set in the MSB half. The test will always pass. + msb_head = lsb_head; + } else if (msb_bit_count == 1) { + // Exactly one bit is set in the MSB half. We can use the BPF_JSET + // operator. + msb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, + msb_bits, + lsb_head, + RetExpression(gen, *cond.failed_)); + gen->JoinInstructions(msb_head, msb_tail); + } else { + // More than one bit is set in the MSB half. We need to combine + // BPF_AND and BPF_JEQ to test whether all of these bits are in fact + // set in the system call argument. + gen->JoinInstructions( + msb_head, + gen->MakeInstruction( + BPF_ALU + BPF_AND + BPF_K, + msb_bits, + gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, + msb_bits, + lsb_head, + RetExpression(gen, *cond.failed_)))); + } } - } - break; - case ErrorCode::OP_HAS_ANY_BITS: - // Check the bits in the LSB half of the system call argument. Our - // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps - // nicely to the kernel's BPF_JSET operation. - { - uint32_t lsb_bits = static_cast(cond.value_); - if (!lsb_bits) { - // No bits are set in the LSB half. The test will always fail. - lsb_head = RetExpression(gen, *cond.failed_); - lsb_tail = NULL; - } else { - lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, - lsb_bits, - RetExpression(gen, *cond.passed_), - RetExpression(gen, *cond.failed_)); - gen->JoinInstructions(lsb_head, lsb_tail); + break; + case ErrorCode::OP_HAS_ANY_BITS: + // Check the bits in the LSB half of the system call argument. Our + // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps + // nicely to the kernel's BPF_JSET operation. + { + uint32_t lsb_bits = static_cast(cond.value_); + if (!lsb_bits) { + // No bits are set in the LSB half. The test will always fail. + lsb_head = RetExpression(gen, *cond.failed_); + lsb_tail = NULL; + } else { + lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, + lsb_bits, + RetExpression(gen, *cond.passed_), + RetExpression(gen, *cond.failed_)); + gen->JoinInstructions(lsb_head, lsb_tail); + } } - } - // If we are looking at a 64bit argument, we need to also check the bits - // in the MSB half of the system call argument. - if (cond.width_ == ErrorCode::TP_64BIT) { - uint32_t msb_bits = static_cast(cond.value_ >> 32); - if (!msb_bits) { - // No bits are set in the MSB half. The test will always fail. - msb_head = lsb_head; - } else { - msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, - msb_bits, - RetExpression(gen, *cond.passed_), - lsb_head); - gen->JoinInstructions(msb_head, msb_tail); + // If we are looking at a 64bit argument, we need to also check the bits + // in the MSB half of the system call argument. + if (cond.width_ == ErrorCode::TP_64BIT) { + uint32_t msb_bits = static_cast(cond.value_ >> 32); + if (!msb_bits) { + // No bits are set in the MSB half. The test will always fail. + msb_head = lsb_head; + } else { + msb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, + msb_bits, + RetExpression(gen, *cond.passed_), + lsb_head); + gen->JoinInstructions(msb_head, msb_tail); + } } - } - break; - default: - // TODO(markus): Need to add support for OP_GREATER - SANDBOX_DIE("Not implemented"); - break; + break; + default: + // TODO(markus): Need to add support for OP_GREATER + SANDBOX_DIE("Not implemented"); + break; } // Ensure that we never pass a 64bit value, when we only expect a 32bit @@ -921,44 +950,46 @@ Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) { // LSB has been sign-extended into the MSB. if (cond.width_ == ErrorCode::TP_32BIT) { if (cond.value_ >> 32) { - SANDBOX_DIE("Invalid comparison of a 32bit system call argument " - "against a 64bit constant; this test is always false."); + SANDBOX_DIE( + "Invalid comparison of a 32bit system call argument " + "against a 64bit constant; this test is always false."); } - Instruction *invalid_64bit = RetExpression(gen, Unexpected64bitArgument()); - #if __SIZEOF_POINTER__ > 4 - invalid_64bit = - gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0xFFFFFFFF, - gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, - SECCOMP_ARG_LSB_IDX(cond.argno_), - gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, 0x80000000, - lsb_head, - invalid_64bit)), - invalid_64bit); - #endif + Instruction* invalid_64bit = RetExpression(gen, Unexpected64bitArgument()); +#if __SIZEOF_POINTER__ > 4 + invalid_64bit = gen->MakeInstruction( + BPF_JMP + BPF_JEQ + BPF_K, + 0xFFFFFFFF, + gen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, + SECCOMP_ARG_LSB_IDX(cond.argno_), + gen->MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, + 0x80000000, + lsb_head, + invalid_64bit)), + invalid_64bit); +#endif gen->JoinInstructions( - msb_tail, - gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0, - lsb_head, - invalid_64bit)); + msb_tail, + gen->MakeInstruction( + BPF_JMP + BPF_JEQ + BPF_K, 0, lsb_head, invalid_64bit)); } return msb_head; } -ErrorCode Sandbox::Unexpected64bitArgument() { +ErrorCode SandboxBPF::Unexpected64bitArgument() { return Kill("Unexpected 64bit argument detected"); } -ErrorCode Sandbox::Trap(Trap::TrapFnc fnc, const void *aux) { +ErrorCode SandboxBPF::Trap(Trap::TrapFnc fnc, const void* aux) { return Trap::MakeTrap(fnc, aux, true /* Safe Trap */); } -ErrorCode Sandbox::UnsafeTrap(Trap::TrapFnc fnc, const void *aux) { +ErrorCode SandboxBPF::UnsafeTrap(Trap::TrapFnc fnc, const void* aux) { return Trap::MakeTrap(fnc, aux, false /* Unsafe Trap */); } -intptr_t Sandbox::ForwardSyscall(const struct arch_seccomp_data& args) { +intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { return SandboxSyscall(args.nr, static_cast(args.args[0]), static_cast(args.args[1]), @@ -968,18 +999,24 @@ intptr_t Sandbox::ForwardSyscall(const struct arch_seccomp_data& args) { static_cast(args.args[5])); } -ErrorCode Sandbox::Cond(int argno, ErrorCode::ArgType width, - ErrorCode::Operation op, uint64_t value, - const ErrorCode& passed, const ErrorCode& failed) { - return ErrorCode(argno, width, op, value, +ErrorCode SandboxBPF::Cond(int argno, + ErrorCode::ArgType width, + ErrorCode::Operation op, + uint64_t value, + const ErrorCode& passed, + const ErrorCode& failed) { + return ErrorCode(argno, + width, + op, + value, &*conds_->insert(passed).first, &*conds_->insert(failed).first); } -ErrorCode Sandbox::Kill(const char *msg) { - return Trap(BpfFailure, const_cast(msg)); +ErrorCode SandboxBPF::Kill(const char* msg) { + return Trap(BPFFailure, const_cast(msg)); } -Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; +SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN; -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h index f2653b077ac..d626e4c74ca 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -16,32 +16,32 @@ #include #include +#include "base/memory/scoped_ptr.h" #include "sandbox/linux/seccomp-bpf/die.h" #include "sandbox/linux/seccomp-bpf/errorcode.h" #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/seccomp-bpf/port.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h" -namespace playground2 { +namespace sandbox { struct arch_seccomp_data { - int nr; + int nr; uint32_t arch; uint64_t instruction_pointer; uint64_t args[6]; }; struct arch_sigsys { - void *ip; - int nr; + void* ip; + int nr; unsigned int arch; }; class CodeGen; +class SandboxBPFPolicy; class SandboxUnittestHelper; struct Instruction; -class Sandbox { +class SandboxBPF { public: enum SandboxStatus { STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox() @@ -51,16 +51,15 @@ class Sandbox { STATUS_ENABLED // The sandbox is now active }; - // BpfSandboxPolicy is the following type: - // ErrorCode (Sandbox *sb, int sysnum, void *aux); // When calling setSandboxPolicy(), the caller can provide an arbitrary // pointer in |aux|. This pointer will then be forwarded to the sandbox // policy each time a call is made through an EvaluateSyscall function // pointer. One common use case would be to pass the "aux" pointer as an // argument to Trap() functions. - typedef BpfSandboxPolicy* EvaluateSyscall; - typedef std::vector >Evaluators; - + typedef ErrorCode (*EvaluateSyscall)(SandboxBPF* sandbox_compiler, + int system_call_number, + void* aux); + typedef std::vector > Evaluators; // A vector of BPF instructions that need to be installed as a filter // program in the kernel. typedef std::vector Program; @@ -75,8 +74,8 @@ class Sandbox { // should be noted that during its lifetime, the object probably made // irreversible state changes to the runtime environment. These changes // stay in effect even after the destructor has been run. - Sandbox(); - ~Sandbox(); + SandboxBPF(); + ~SandboxBPF(); // Checks whether a particular system call number is valid on the current // architecture. E.g. on ARM there's a non-contiguous range of private @@ -108,7 +107,12 @@ class Sandbox { // handler. In this case, of course, the data that is pointed to must remain // valid for the entire time that Trap() handlers can be called; typically, // this would be the lifetime of the program. - void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux); + // DEPRECATED: use the policy interface below. + void SetSandboxPolicyDeprecated(EvaluateSyscall syscallEvaluator, void* aux); + + // Set the BPF policy as |policy|. Ownership of |policy| is transfered here + // to the sandbox object. + void SetSandboxPolicy(SandboxBPFPolicy* policy); // We can use ErrorCode to request calling of a trap handler. This method // performs the required wrapping of the callback function into an @@ -116,7 +120,7 @@ class Sandbox { // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall // for a description of how to pass data from SetSandboxPolicy() to a Trap() // handler. - ErrorCode Trap(Trap::TrapFnc fnc, const void *aux); + ErrorCode Trap(Trap::TrapFnc fnc, const void* aux); // Calls a user-space trap handler and disables all sandboxing for system // calls made from this trap handler. @@ -128,7 +132,7 @@ class Sandbox { // very useful to diagnose code that is incompatible with the sandbox. // If even a single system call returns "UnsafeTrap", the security of // entire sandbox should be considered compromised. - ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux); + ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void* aux); // From within an UnsafeTrap() it is often useful to be able to execute // the system call that triggered the trap. The ForwardSyscall() method @@ -150,13 +154,15 @@ class Sandbox { // If it is outside this range, the sandbox treats the system call just // the same as any other ABI violation (i.e. it aborts with an error // message). - ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit, + ErrorCode Cond(int argno, + ErrorCode::ArgType is_32bit, ErrorCode::Operation op, - uint64_t value, const ErrorCode& passed, + uint64_t value, + const ErrorCode& passed, const ErrorCode& failed); // Kill the program and print an error message. - ErrorCode Kill(const char *msg); + ErrorCode Kill(const char* msg); // This is the main public entry point. It finds all system calls that // need rewriting, sets up the resources needed by the sandbox, and @@ -179,7 +185,7 @@ class Sandbox { // through the verifier, iff the program was built in debug mode. // But by setting "force_verification", the caller can request that the // verifier is run unconditionally. This is useful for unittests. - Program *AssembleFilter(bool force_verification); + Program* AssembleFilter(bool force_verification); // Returns the fatal ErrorCode that is used to indicate that somebody // attempted to pass a 64bit value in a 32bit system call argument. @@ -193,11 +199,8 @@ class Sandbox { struct Range { Range(uint32_t f, uint32_t t, const ErrorCode& e) - : from(f), - to(t), - err(e) { - } - uint32_t from, to; + : from(f), to(t), err(e) {} + uint32_t from, to; ErrorCode err; }; typedef std::vector Ranges; @@ -211,7 +214,8 @@ class Sandbox { // policy. The caller has to make sure that "this" has not yet been // initialized with any other policies. bool RunFunctionInPolicy(void (*code_in_sandbox)(), - EvaluateSyscall syscall_evaluator, void *aux); + EvaluateSyscall syscall_evaluator, + void* aux); // Performs a couple of sanity checks to verify that the kernel supports the // features that we need for successful sandboxing. @@ -220,7 +224,7 @@ class Sandbox { bool KernelSupportSeccompBPF(); // Verify that the current policy passes some basic sanity checks. - void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux); + void PolicySanityChecks(SandboxBPFPolicy* policy); // Assembles and installs a filter based on the policy that has previously // been configured with SetSandboxPolicy(). @@ -235,11 +239,11 @@ class Sandbox { // sorted in ascending order of system call numbers. There are no gaps in the // ranges. System calls with identical ErrorCodes are coalesced into a single // range. - void FindRanges(Ranges *ranges); + void FindRanges(Ranges* ranges); // Returns a BPF program snippet that implements a jump table for the // given range of system call numbers. This function runs recursively. - Instruction *AssembleJumpTable(CodeGen *gen, + Instruction* AssembleJumpTable(CodeGen* gen, Ranges::const_iterator start, Ranges::const_iterator stop); @@ -248,24 +252,25 @@ class Sandbox { // conditional expression; if so, this function will recursively call // CondExpression() and possibly RetExpression() to build a complex set of // instructions. - Instruction *RetExpression(CodeGen *gen, const ErrorCode& err); + Instruction* RetExpression(CodeGen* gen, const ErrorCode& err); // Returns a BPF program that evaluates the conditional expression in // "cond" and returns the appropriate value from the BPF filter program. // This function recursively calls RetExpression(); it should only ever be // called from RetExpression(). - Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond); + Instruction* CondExpression(CodeGen* gen, const ErrorCode& cond); static SandboxStatus status_; - bool quiet_; - int proc_fd_; - Evaluators *evaluators_; - Conds *conds_; + bool quiet_; + int proc_fd_; + scoped_ptr policy_; + Conds* conds_; + bool sandbox_has_started_; - DISALLOW_COPY_AND_ASSIGN(Sandbox); + DISALLOW_COPY_AND_ASSIGN(SandboxBPF); }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h new file mode 100644 index 00000000000..1ac5daba5d9 --- /dev/null +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h @@ -0,0 +1,35 @@ +// Copyright 2013 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_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_ + +#include "base/basictypes.h" + +namespace sandbox { + +class ErrorCode; +class SandboxBPF; + +// This is the interface to implement to define a BPF sandbox policy. +class SandboxBPFPolicy { + public: + SandboxBPFPolicy() {} + virtual ~SandboxBPFPolicy() {} + + // The EvaluateSyscall method is called with the system call number. It can + // decide to allow the system call unconditionally by returning ERR_ALLOWED; + // it can deny the system call unconditionally by returning an appropriate + // "errno" value; or it can request inspection of system call argument(s) by + // returning a suitable ErrorCode. + virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, + int system_call_number) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SandboxBPFPolicy); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h deleted file mode 100644 index afc9d870368..00000000000 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 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_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_FORWARD_H_ -#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_FORWARD_H_ - -#include "base/callback_forward.h" - -namespace playground2 { - -class Sandbox; -class ErrorCode; -typedef ErrorCode BpfSandboxPolicy( - Sandbox* sandbox_compiler, - int system_call_number, - void* aux); - -typedef base::Callback BpfSandboxPolicyCallback; - -} // namespace playground2 - -#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_FORWARD_H_ diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc index 1713e8f2029..988e29544b0 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc @@ -33,42 +33,40 @@ // Workaround for Android's prctl.h file. #ifndef PR_GET_ENDIAN -#define PR_GET_ENDIAN 19 +#define PR_GET_ENDIAN 19 #endif #ifndef PR_CAPBSET_READ #define PR_CAPBSET_READ 23 #define PR_CAPBSET_DROP 24 #endif -using namespace playground2; -using sandbox::BrokerProcess; +namespace sandbox { namespace { -const int kExpectedReturnValue = 42; +const int kExpectedReturnValue = 42; const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING"; // This test should execute no matter whether we have kernel support. So, // we make it a TEST() instead of a BPF_TEST(). -TEST(SandboxBpf, CallSupports) { +TEST(SandboxBPF, CallSupports) { // We check that we don't crash, but it's ok if the kernel doesn't // support it. bool seccomp_bpf_supported = - Sandbox::SupportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE; + SandboxBPF::SupportsSeccompSandbox(-1) == SandboxBPF::STATUS_AVAILABLE; // We want to log whether or not seccomp BPF is actually supported // since actual test coverage depends on it. RecordProperty("SeccompBPFSupported", seccomp_bpf_supported ? "true." : "false."); std::cout << "Seccomp BPF supported: " - << (seccomp_bpf_supported ? "true." : "false.") - << "\n"; + << (seccomp_bpf_supported ? "true." : "false.") << "\n"; RecordProperty("PointerSize", sizeof(void*)); std::cout << "Pointer size: " << sizeof(void*) << "\n"; } -SANDBOX_TEST(SandboxBpf, CallSupportsTwice) { - Sandbox::SupportsSeccompSandbox(-1); - Sandbox::SupportsSeccompSandbox(-1); +SANDBOX_TEST(SandboxBPF, CallSupportsTwice) { + SandboxBPF::SupportsSeccompSandbox(-1); + SandboxBPF::SupportsSeccompSandbox(-1); } // BPF_TEST does a lot of the boiler-plate code around setting up a @@ -78,14 +76,14 @@ SANDBOX_TEST(SandboxBpf, CallSupportsTwice) { // setting up the sandbox. But it wouldn't hurt to have at least one test // that explicitly walks through all these steps. -intptr_t FakeGetPid(const struct arch_seccomp_data& args, void *aux) { +intptr_t FakeGetPid(const struct arch_seccomp_data& args, void* aux) { BPF_ASSERT(aux); - pid_t *pid_ptr = static_cast(aux); + pid_t* pid_ptr = static_cast(aux); return (*pid_ptr)++; } -ErrorCode VerboseAPITestingPolicy(Sandbox *sandbox, int sysno, void *aux) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode VerboseAPITestingPolicy(SandboxBPF* sandbox, int sysno, void* aux) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } else if (sysno == __NR_getpid) { return sandbox->Trap(FakeGetPid, aux); @@ -94,12 +92,12 @@ ErrorCode VerboseAPITestingPolicy(Sandbox *sandbox, int sysno, void *aux) { } } -SANDBOX_TEST(SandboxBpf, DISABLE_ON_TSAN(VerboseAPITesting)) { - if (Sandbox::SupportsSeccompSandbox(-1) == - playground2::Sandbox::STATUS_AVAILABLE) { +SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) { + if (SandboxBPF::SupportsSeccompSandbox(-1) == + sandbox::SandboxBPF::STATUS_AVAILABLE) { pid_t test_var = 0; - Sandbox sandbox; - sandbox.SetSandboxPolicy(VerboseAPITestingPolicy, &test_var); + SandboxBPF sandbox; + sandbox.SetSandboxPolicyDeprecated(VerboseAPITestingPolicy, &test_var); sandbox.StartSandbox(); BPF_ASSERT(test_var == 0); @@ -116,8 +114,8 @@ SANDBOX_TEST(SandboxBpf, DISABLE_ON_TSAN(VerboseAPITesting)) { // A simple blacklist test -ErrorCode BlacklistNanosleepPolicy(Sandbox *, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode BlacklistNanosleepPolicy(SandboxBPF*, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } @@ -130,7 +128,7 @@ ErrorCode BlacklistNanosleepPolicy(Sandbox *, int sysno, void *) { } } -BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) { +BPF_TEST(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) { // nanosleep() should be denied const struct timespec ts = {0, 0}; errno = 0; @@ -140,7 +138,7 @@ BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) { // Now do a simple whitelist test -ErrorCode WhitelistGetpidPolicy(Sandbox *, int sysno, void *) { +ErrorCode WhitelistGetpidPolicy(SandboxBPF*, int sysno, void*) { switch (sysno) { case __NR_getpid: case __NR_exit_group: @@ -150,7 +148,7 @@ ErrorCode WhitelistGetpidPolicy(Sandbox *, int sysno, void *) { } } -BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) { +BPF_TEST(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) { // getpid() should be allowed errno = 0; BPF_ASSERT(syscall(__NR_getpid) > 0); @@ -163,16 +161,17 @@ BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) { // A simple blacklist policy, with a SIGSYS handler -intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) { +intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) { // We also check that the auxiliary data is correct SANDBOX_ASSERT(aux); *(static_cast(aux)) = kExpectedReturnValue; return -ENOMEM; } -ErrorCode BlacklistNanosleepPolicySigsys(Sandbox *sandbox, int sysno, - void *aux) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode BlacklistNanosleepPolicySigsys(SandboxBPF* sandbox, + int sysno, + void* aux) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } @@ -185,8 +184,10 @@ ErrorCode BlacklistNanosleepPolicySigsys(Sandbox *sandbox, int sysno, } } -BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys, - BlacklistNanosleepPolicySigsys, int /* BPF_AUX */) { +BPF_TEST(SandboxBPF, + BasicBlacklistWithSigsys, + BlacklistNanosleepPolicySigsys, + int /* BPF_AUX */) { // getpid() should work properly errno = 0; BPF_ASSERT(syscall(__NR_getpid) > 0); @@ -204,43 +205,43 @@ BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys, // A simple test that verifies we can return arbitrary errno values. -ErrorCode ErrnoTestPolicy(Sandbox *, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode ErrnoTestPolicy(SandboxBPF*, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } switch (sysno) { - case __NR_dup2: - // Pretend that dup2() worked, but don't actually do anything. - return ErrorCode(0); - case __NR_setuid: + case __NR_dup2: + // Pretend that dup2() worked, but don't actually do anything. + return ErrorCode(0); + case __NR_setuid: #if defined(__NR_setuid32) - case __NR_setuid32: + case __NR_setuid32: #endif - // Return errno = 1. - return ErrorCode(1); - case __NR_setgid: + // Return errno = 1. + return ErrorCode(1); + case __NR_setgid: #if defined(__NR_setgid32) - case __NR_setgid32: + case __NR_setgid32: #endif - // Return maximum errno value (typically 4095). - return ErrorCode(ErrorCode::ERR_MAX_ERRNO); - case __NR_uname: - // Return errno = 42; - return ErrorCode(42); - default: - return ErrorCode(ErrorCode::ERR_ALLOWED); + // Return maximum errno value (typically 4095). + return ErrorCode(ErrorCode::ERR_MAX_ERRNO); + case __NR_uname: + // Return errno = 42; + return ErrorCode(42); + default: + return ErrorCode(ErrorCode::ERR_ALLOWED); } } -BPF_TEST(SandboxBpf, ErrnoTest, ErrnoTestPolicy) { +BPF_TEST(SandboxBPF, ErrnoTest, ErrnoTestPolicy) { // Verify that dup2() returns success, but doesn't actually run. int fds[4]; BPF_ASSERT(pipe(fds) == 0); - BPF_ASSERT(pipe(fds+2) == 0); + BPF_ASSERT(pipe(fds + 2) == 0); BPF_ASSERT(dup2(fds[2], fds[0]) == 0); - char buf[1] = { }; + char buf[1] = {}; BPF_ASSERT(write(fds[1], "\x55", 1) == 1); BPF_ASSERT(write(fds[3], "\xAA", 1) == 1); BPF_ASSERT(read(fds[0], buf, 1) == 1); @@ -276,14 +277,17 @@ BPF_TEST(SandboxBpf, ErrnoTest, ErrnoTestPolicy) { // Testing the stacking of two sandboxes -ErrorCode StackingPolicyPartOne(Sandbox *sandbox, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode StackingPolicyPartOne(SandboxBPF* sandbox, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } switch (sysno) { case __NR_getppid: - return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0, + return sandbox->Cond(0, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + 0, ErrorCode(ErrorCode::ERR_ALLOWED), ErrorCode(EPERM)); default: @@ -291,14 +295,17 @@ ErrorCode StackingPolicyPartOne(Sandbox *sandbox, int sysno, void *) { } } -ErrorCode StackingPolicyPartTwo(Sandbox *sandbox, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode StackingPolicyPartTwo(SandboxBPF* sandbox, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } switch (sysno) { case __NR_getppid: - return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0, + return sandbox->Cond(0, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + 0, ErrorCode(EINVAL), ErrorCode(ErrorCode::ERR_ALLOWED)); default: @@ -306,7 +313,7 @@ ErrorCode StackingPolicyPartTwo(Sandbox *sandbox, int sysno, void *) { } } -BPF_TEST(SandboxBpf, StackingPolicy, StackingPolicyPartOne) { +BPF_TEST(SandboxBPF, StackingPolicy, StackingPolicyPartOne) { errno = 0; BPF_ASSERT(syscall(__NR_getppid, 0) > 0); BPF_ASSERT(errno == 0); @@ -316,8 +323,8 @@ BPF_TEST(SandboxBpf, StackingPolicy, StackingPolicyPartOne) { // Stack a second sandbox with its own policy. Verify that we can further // restrict filters, but we cannot relax existing filters. - Sandbox sandbox; - sandbox.SetSandboxPolicy(StackingPolicyPartTwo, NULL); + SandboxBPF sandbox; + sandbox.SetSandboxPolicyDeprecated(StackingPolicyPartTwo, NULL); sandbox.StartSandbox(); errno = 0; @@ -343,8 +350,8 @@ int SysnoToRandomErrno(int sysno) { return ((sysno & ~3) >> 2) % 29 + 1; } -ErrorCode SyntheticPolicy(Sandbox *, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode SyntheticPolicy(SandboxBPF*, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } @@ -365,18 +372,16 @@ ErrorCode SyntheticPolicy(Sandbox *, int sysno, void *) { } } -BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) { +BPF_TEST(SandboxBPF, SyntheticPolicy, SyntheticPolicy) { // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int // overflow. - BPF_ASSERT( - std::numeric_limits::max() - kExpectedReturnValue - 1 >= - static_cast(MAX_PUBLIC_SYSCALL)); - - for (int syscall_number = static_cast(MIN_SYSCALL); - syscall_number <= static_cast(MAX_PUBLIC_SYSCALL); - ++syscall_number) { - if (syscall_number == __NR_exit_group || - syscall_number == __NR_write) { + BPF_ASSERT(std::numeric_limits::max() - kExpectedReturnValue - 1 >= + static_cast(MAX_PUBLIC_SYSCALL)); + + for (int syscall_number = static_cast(MIN_SYSCALL); + syscall_number <= static_cast(MAX_PUBLIC_SYSCALL); + ++syscall_number) { + if (syscall_number == __NR_exit_group || syscall_number == __NR_write) { // exit_group() is special continue; } @@ -401,8 +406,8 @@ int ArmPrivateSysnoToErrno(int sysno) { } } -ErrorCode ArmPrivatePolicy(Sandbox *, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode ArmPrivatePolicy(SandboxBPF*, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy. return ErrorCode(ENOSYS); } @@ -417,10 +422,10 @@ ErrorCode ArmPrivatePolicy(Sandbox *, int sysno, void *) { } } -BPF_TEST(SandboxBpf, ArmPrivatePolicy, ArmPrivatePolicy) { - for (int syscall_number = static_cast(__ARM_NR_set_tls + 1); - syscall_number <= static_cast(MAX_PRIVATE_SYSCALL); - ++syscall_number) { +BPF_TEST(SandboxBPF, ArmPrivatePolicy, ArmPrivatePolicy) { + for (int syscall_number = static_cast(__ARM_NR_set_tls + 1); + syscall_number <= static_cast(MAX_PRIVATE_SYSCALL); + ++syscall_number) { errno = 0; BPF_ASSERT(syscall(syscall_number) == -1); BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number)); @@ -428,9 +433,9 @@ BPF_TEST(SandboxBpf, ArmPrivatePolicy, ArmPrivatePolicy) { } #endif // defined(__arm__) -intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) { +intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) { // Count all invocations of our callback function. - ++*reinterpret_cast(aux); + ++*reinterpret_cast(aux); // Verify that within the callback function all filtering is temporarily // disabled. @@ -438,10 +443,10 @@ intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) { // Verify that we can now call the underlying system call without causing // infinite recursion. - return Sandbox::ForwardSyscall(args); + return SandboxBPF::ForwardSyscall(args); } -ErrorCode GreyListedPolicy(Sandbox *sandbox, int sysno, void *aux) { +ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, void* aux) { // The use of UnsafeTrap() causes us to print a warning message. This is // generally desirable, but it results in the unittest failing, as it doesn't // expect any messages on "stderr". So, temporarily disable messages. The @@ -452,42 +457,46 @@ ErrorCode GreyListedPolicy(Sandbox *sandbox, int sysno, void *aux) { // Some system calls must always be allowed, if our policy wants to make // use of UnsafeTrap() - if (sysno == __NR_rt_sigprocmask || - sysno == __NR_rt_sigreturn + if (sysno == __NR_rt_sigprocmask || sysno == __NR_rt_sigreturn #if defined(__NR_sigprocmask) - || sysno == __NR_sigprocmask + || + sysno == __NR_sigprocmask #endif #if defined(__NR_sigreturn) - || sysno == __NR_sigreturn + || + sysno == __NR_sigreturn #endif ) { return ErrorCode(ErrorCode::ERR_ALLOWED); } else if (sysno == __NR_getpid) { // Disallow getpid() return ErrorCode(EPERM); - } else if (Sandbox::IsValidSyscallNumber(sysno)) { + } else if (SandboxBPF::IsValidSyscallNumber(sysno)) { // Allow (and count) all other system calls. - return sandbox->UnsafeTrap(CountSyscalls, aux); + return sandbox->UnsafeTrap(CountSyscalls, aux); } else { return ErrorCode(ENOSYS); } } -BPF_TEST(SandboxBpf, GreyListedPolicy, - GreyListedPolicy, int /* BPF_AUX */) { +BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* BPF_AUX */) { BPF_ASSERT(syscall(__NR_getpid) == -1); BPF_ASSERT(errno == EPERM); BPF_ASSERT(BPF_AUX == 0); BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid)); BPF_ASSERT(BPF_AUX == 2); - char name[17] = { }; - BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL, - (void *)NULL, (void *)NULL)); + char name[17] = {}; + BPF_ASSERT(!syscall(__NR_prctl, + PR_GET_NAME, + name, + (void*)NULL, + (void*)NULL, + (void*)NULL)); BPF_ASSERT(BPF_AUX == 3); BPF_ASSERT(*name); } -SANDBOX_TEST(SandboxBpf, EnableUnsafeTrapsInSigSysHandler) { +SANDBOX_TEST(SandboxBPF, EnableUnsafeTrapsInSigSysHandler) { // Disabling warning messages that could confuse our test framework. setenv(kSandboxDebuggingEnv, "t", 0); Die::SuppressInfoMessages(true); @@ -500,25 +509,24 @@ SANDBOX_TEST(SandboxBpf, EnableUnsafeTrapsInSigSysHandler) { SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == true); } -intptr_t PrctlHandler(const struct arch_seccomp_data& args, void *) { - if (args.args[0] == PR_CAPBSET_DROP && - static_cast(args.args[1]) == -1) { +intptr_t PrctlHandler(const struct arch_seccomp_data& args, void*) { + if (args.args[0] == PR_CAPBSET_DROP && static_cast(args.args[1]) == -1) { // prctl(PR_CAPBSET_DROP, -1) is never valid. The kernel will always // return an error. But our handler allows this call. return 0; } else { - return Sandbox::ForwardSyscall(args); + return SandboxBPF::ForwardSyscall(args); } } -ErrorCode PrctlPolicy(Sandbox *sandbox, int sysno, void *aux) { +ErrorCode PrctlPolicy(SandboxBPF* sandbox, int sysno, void* aux) { setenv(kSandboxDebuggingEnv, "t", 0); Die::SuppressInfoMessages(true); if (sysno == __NR_prctl) { // Handle prctl() inside an UnsafeTrap() return sandbox->UnsafeTrap(PrctlHandler, NULL); - } else if (Sandbox::IsValidSyscallNumber(sysno)) { + } else if (SandboxBPF::IsValidSyscallNumber(sysno)) { // Allow all other system calls. return ErrorCode(ErrorCode::ERR_ALLOWED); } else { @@ -526,50 +534,55 @@ ErrorCode PrctlPolicy(Sandbox *sandbox, int sysno, void *aux) { } } -BPF_TEST(SandboxBpf, ForwardSyscall, PrctlPolicy) { +BPF_TEST(SandboxBPF, ForwardSyscall, PrctlPolicy) { // This call should never be allowed. But our policy will intercept it and // let it pass successfully. - BPF_ASSERT(!prctl(PR_CAPBSET_DROP, -1, (void *)NULL, (void *)NULL, - (void *)NULL)); + BPF_ASSERT( + !prctl(PR_CAPBSET_DROP, -1, (void*)NULL, (void*)NULL, (void*)NULL)); // Verify that the call will fail, if it makes it all the way to the kernel. - BPF_ASSERT(prctl(PR_CAPBSET_DROP, -2, (void *)NULL, (void *)NULL, - (void *)NULL) == -1); + BPF_ASSERT( + prctl(PR_CAPBSET_DROP, -2, (void*)NULL, (void*)NULL, (void*)NULL) == -1); // And verify that other uses of prctl() work just fine. - char name[17] = { }; - BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL, - (void *)NULL, (void *)NULL)); + char name[17] = {}; + BPF_ASSERT(!syscall(__NR_prctl, + PR_GET_NAME, + name, + (void*)NULL, + (void*)NULL, + (void*)NULL)); BPF_ASSERT(*name); // Finally, verify that system calls other than prctl() are completely // unaffected by our policy. - struct utsname uts = { }; + struct utsname uts = {}; BPF_ASSERT(!uname(&uts)); BPF_ASSERT(!strcmp(uts.sysname, "Linux")); } -intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void *) { - return Sandbox::ForwardSyscall(args); +intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void*) { + return SandboxBPF::ForwardSyscall(args); } -ErrorCode RedirectAllSyscallsPolicy(Sandbox *sandbox, int sysno, void *aux) { +ErrorCode RedirectAllSyscallsPolicy(SandboxBPF* sandbox, int sysno, void* aux) { setenv(kSandboxDebuggingEnv, "t", 0); Die::SuppressInfoMessages(true); // Some system calls must always be allowed, if our policy wants to make // use of UnsafeTrap() - if (sysno == __NR_rt_sigprocmask || - sysno == __NR_rt_sigreturn + if (sysno == __NR_rt_sigprocmask || sysno == __NR_rt_sigreturn #if defined(__NR_sigprocmask) - || sysno == __NR_sigprocmask + || + sysno == __NR_sigprocmask #endif #if defined(__NR_sigreturn) - || sysno == __NR_sigreturn + || + sysno == __NR_sigreturn #endif ) { return ErrorCode(ErrorCode::ERR_ALLOWED); - } else if (Sandbox::IsValidSyscallNumber(sysno)) { + } else if (SandboxBPF::IsValidSyscallNumber(sysno)) { return sandbox->UnsafeTrap(AllowRedirectedSyscall, aux); } else { return ErrorCode(ENOSYS); @@ -578,11 +591,11 @@ ErrorCode RedirectAllSyscallsPolicy(Sandbox *sandbox, int sysno, void *aux) { int bus_handler_fd_ = -1; -void SigBusHandler(int, siginfo_t *info, void *void_context) { +void SigBusHandler(int, siginfo_t* info, void* void_context) { BPF_ASSERT(write(bus_handler_fd_, "\x55", 1) == 1); } -BPF_TEST(SandboxBpf, SigBus, RedirectAllSyscallsPolicy) { +BPF_TEST(SandboxBPF, SigBus, RedirectAllSyscallsPolicy) { // We use the SIGBUS bit in the signal mask as a thread-local boolean // value in the implementation of UnsafeTrap(). This is obviously a bit // of a hack that could conceivably interfere with code that uses SIGBUS @@ -593,7 +606,7 @@ BPF_TEST(SandboxBpf, SigBus, RedirectAllSyscallsPolicy) { int fds[2]; BPF_ASSERT(pipe(fds) == 0); bus_handler_fd_ = fds[1]; - struct sigaction sa = { }; + struct sigaction sa = {}; sa.sa_sigaction = SigBusHandler; sa.sa_flags = SA_SIGINFO; BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0); @@ -605,7 +618,7 @@ BPF_TEST(SandboxBpf, SigBus, RedirectAllSyscallsPolicy) { BPF_ASSERT(c == 0x55); } -BPF_TEST(SandboxBpf, SigMask, RedirectAllSyscallsPolicy) { +BPF_TEST(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) { // Signal masks are potentially tricky to handle. For instance, if we // ever tried to update them from inside a Trap() or UnsafeTrap() handler, // the call to sigreturn() at the end of the signal handler would undo @@ -629,10 +642,10 @@ BPF_TEST(SandboxBpf, SigMask, RedirectAllSyscallsPolicy) { sigaddset(&mask0, SIGUSR2); BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL)); BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2)); - BPF_ASSERT( sigismember(&mask2, SIGUSR2)); + BPF_ASSERT(sigismember(&mask2, SIGUSR2)); } -BPF_TEST(SandboxBpf, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) { +BPF_TEST(SandboxBPF, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) { // An UnsafeTrap() (or for that matter, a Trap()) has to report error // conditions by returning an exit code in the range -1..-4096. This // should happen automatically if using ForwardSyscall(). If the TrapFnc() @@ -650,10 +663,10 @@ BPF_TEST(SandboxBpf, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) { // would make system calls, but it allows us to verify that we don't // accidentally mess with errno, when we shouldn't. errno = 0; - struct arch_seccomp_data args = { }; - args.nr = __NR_close; + struct arch_seccomp_data args = {}; + args.nr = __NR_close; args.args[0] = -1; - BPF_ASSERT(Sandbox::ForwardSyscall(args) == -EBADF); + BPF_ASSERT(SandboxBPF::ForwardSyscall(args) == -EBADF); BPF_ASSERT(errno == 0); } @@ -666,8 +679,8 @@ class InitializedOpenBroker { allowed_files.push_back("/proc/allowed"); allowed_files.push_back("/proc/cpuinfo"); - broker_process_.reset(new BrokerProcess(allowed_files, - std::vector())); + broker_process_.reset( + new BrokerProcess(EPERM, allowed_files, std::vector())); BPF_ASSERT(broker_process() != NULL); BPF_ASSERT(broker_process_->Init(NULL)); @@ -675,6 +688,7 @@ class InitializedOpenBroker { } bool initialized() { return initialized_; } class BrokerProcess* broker_process() { return broker_process_.get(); } + private: bool initialized_; scoped_ptr broker_process_; @@ -682,31 +696,31 @@ class InitializedOpenBroker { }; intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args, - void *aux) { + void* aux) { BPF_ASSERT(aux); BrokerProcess* broker_process = static_cast(aux); - switch(args.nr) { + switch (args.nr) { case __NR_access: return broker_process->Access(reinterpret_cast(args.args[0]), - static_cast(args.args[1])); + static_cast(args.args[1])); case __NR_open: return broker_process->Open(reinterpret_cast(args.args[0]), - static_cast(args.args[1])); + static_cast(args.args[1])); case __NR_openat: // We only call open() so if we arrive here, it's because glibc uses // the openat() system call. BPF_ASSERT(static_cast(args.args[0]) == AT_FDCWD); return broker_process->Open(reinterpret_cast(args.args[1]), - static_cast(args.args[2])); + static_cast(args.args[2])); default: BPF_ASSERT(false); return -ENOSYS; } } -ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) { +ErrorCode DenyOpenPolicy(SandboxBPF* sandbox, int sysno, void* aux) { InitializedOpenBroker* iob = static_cast(aux); - if (!Sandbox::IsValidSyscallNumber(sysno)) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } @@ -716,8 +730,8 @@ ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) { case __NR_openat: // We get a InitializedOpenBroker class, but our trap handler wants // the BrokerProcess object. - return ErrorCode(sandbox->Trap(BrokerOpenTrapHandler, - iob->broker_process())); + return ErrorCode( + sandbox->Trap(BrokerOpenTrapHandler, iob->broker_process())); default: return ErrorCode(ErrorCode::ERR_ALLOWED); } @@ -725,10 +739,12 @@ ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) { // We use a InitializedOpenBroker class, so that we can run unsandboxed // code in its constructor, which is the only way to do so in a BPF_TEST. -BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy, +BPF_TEST(SandboxBPF, + UseOpenBroker, + DenyOpenPolicy, InitializedOpenBroker /* BPF_AUX */) { BPF_ASSERT(BPF_AUX.initialized()); - BrokerProcess* broker_process = BPF_AUX.broker_process(); + BrokerProcess* broker_process = BPF_AUX.broker_process(); BPF_ASSERT(broker_process != NULL); // First, use the broker "manually" @@ -768,10 +784,10 @@ BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy, BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0); } -// Simple test demonstrating how to use Sandbox::Cond() +// Simple test demonstrating how to use SandboxBPF::Cond() -ErrorCode SimpleCondTestPolicy(Sandbox *sandbox, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode SimpleCondTestPolicy(SandboxBPF* sandbox, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } @@ -783,26 +799,32 @@ ErrorCode SimpleCondTestPolicy(Sandbox *sandbox, int sysno, void *) { case __NR_open: // Allow opening files for reading, but don't allow writing. COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits); - return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, + return sandbox->Cond(1, + ErrorCode::TP_32BIT, + ErrorCode::OP_HAS_ANY_BITS, O_ACCMODE /* 0x3 */, ErrorCode(EROFS), ErrorCode(ErrorCode::ERR_ALLOWED)); case __NR_prctl: // Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but // disallow everything else. - return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + return sandbox->Cond(0, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, PR_SET_DUMPABLE, ErrorCode(ErrorCode::ERR_ALLOWED), - sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - PR_GET_DUMPABLE, - ErrorCode(ErrorCode::ERR_ALLOWED), - ErrorCode(ENOMEM))); + sandbox->Cond(0, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + PR_GET_DUMPABLE, + ErrorCode(ErrorCode::ERR_ALLOWED), + ErrorCode(ENOMEM))); default: return ErrorCode(ErrorCode::ERR_ALLOWED); } } -BPF_TEST(SandboxBpf, SimpleCondTest, SimpleCondTestPolicy) { +BPF_TEST(SandboxBPF, SimpleCondTest, SimpleCondTestPolicy) { int fd; BPF_ASSERT((fd = open("/proc/self/comm", O_RDWR)) == -1); BPF_ASSERT(errno == EROFS); @@ -811,12 +833,12 @@ BPF_TEST(SandboxBpf, SimpleCondTest, SimpleCondTestPolicy) { int ret; BPF_ASSERT((ret = prctl(PR_GET_DUMPABLE)) >= 0); - BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1-ret) == 0); + BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1 - ret) == 0); BPF_ASSERT(prctl(PR_GET_ENDIAN, &ret) == -1); BPF_ASSERT(errno == ENOMEM); } -// This test exercises the Sandbox::Cond() method by building a complex +// This test exercises the SandboxBPF::Cond() method by building a complex // tree of conditional equality operations. It then makes system calls and // verifies that they return the values that we expected from our BPF // program. @@ -831,8 +853,9 @@ class EqualityStressTest { // We are actually constructing a graph of ArgValue objects. This // graph will later be used to a) compute our sandbox policy, and // b) drive the code that verifies the output from the BPF program. - COMPILE_ASSERT(kNumTestCases < (int)(MAX_PUBLIC_SYSCALL-MIN_SYSCALL-10), - num_test_cases_must_be_significantly_smaller_than_num_system_calls); + COMPILE_ASSERT( + kNumTestCases < (int)(MAX_PUBLIC_SYSCALL - MIN_SYSCALL - 10), + num_test_cases_must_be_significantly_smaller_than_num_system_calls); for (int sysno = MIN_SYSCALL, end = kNumTestCases; sysno < end; ++sysno) { if (IsReservedSyscall(sysno)) { // Skip reserved system calls. This ensures that our test frame @@ -841,22 +864,22 @@ class EqualityStressTest { ++end; arg_values_.push_back(NULL); } else { - arg_values_.push_back(RandomArgValue(rand() % kMaxArgs, 0, - rand() % kMaxArgs)); + arg_values_.push_back( + RandomArgValue(rand() % kMaxArgs, 0, rand() % kMaxArgs)); } } } ~EqualityStressTest() { - for (std::vector::iterator iter = arg_values_.begin(); + for (std::vector::iterator iter = arg_values_.begin(); iter != arg_values_.end(); ++iter) { DeleteArgValue(*iter); } } - ErrorCode Policy(Sandbox *sandbox, int sysno) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { + ErrorCode Policy(SandboxBPF* sandbox, int sysno) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno < 0 || sysno >= (int)arg_values_.size() || @@ -887,22 +910,22 @@ class EqualityStressTest { // We arbitrarily start by setting all six system call arguments to // zero. And we then recursive traverse our tree of ArgValues to // determine the necessary combinations of parameters. - intptr_t args[6] = { }; + intptr_t args[6] = {}; Verify(sysno, args, *arg_values_[sysno]); } } private: struct ArgValue { - int argno; // Argument number to inspect. - int size; // Number of test cases (must be > 0). + int argno; // Argument number to inspect. + int size; // Number of test cases (must be > 0). struct Tests { uint32_t k_value; // Value to compare syscall arg against. - int err; // If non-zero, errno value to return. - struct ArgValue *arg_value; // Otherwise, more args needs inspecting. - } *tests; - int err; // If none of the tests passed, this is what - struct ArgValue *arg_value; // we'll return (this is the "else" branch). + int err; // If non-zero, errno value to return. + struct ArgValue* arg_value; // Otherwise, more args needs inspecting. + }* tests; + int err; // If none of the tests passed, this is what + struct ArgValue* arg_value; // we'll return (this is the "else" branch). }; bool IsReservedSyscall(int sysno) { @@ -916,41 +939,38 @@ class EqualityStressTest { // calls that will be made by this particular test. So, this small list is // sufficient. But if anybody copy'n'pasted this code for other uses, they // would have to review that the list. - return sysno == __NR_read || - sysno == __NR_write || - sysno == __NR_exit || - sysno == __NR_exit_group || - sysno == __NR_restart_syscall; + return sysno == __NR_read || sysno == __NR_write || sysno == __NR_exit || + sysno == __NR_exit_group || sysno == __NR_restart_syscall; } - ArgValue *RandomArgValue(int argno, int args_mask, int remaining_args) { + ArgValue* RandomArgValue(int argno, int args_mask, int remaining_args) { // Create a new ArgValue and fill it with random data. We use as bit mask // to keep track of the system call parameters that have previously been // set; this ensures that we won't accidentally define a contradictory // set of equality tests. - struct ArgValue *arg_value = new ArgValue(); - args_mask |= 1 << argno; - arg_value->argno = argno; + struct ArgValue* arg_value = new ArgValue(); + args_mask |= 1 << argno; + arg_value->argno = argno; // Apply some restrictions on just how complex our tests can be. // Otherwise, we end up with a BPF program that is too complicated for // the kernel to load. - int fan_out = kMaxFanOut; + int fan_out = kMaxFanOut; if (remaining_args > 3) { - fan_out = 1; + fan_out = 1; } else if (remaining_args > 2) { - fan_out = 2; + fan_out = 2; } // Create a couple of different test cases with randomized values that // we want to use when comparing system call parameter number "argno". - arg_value->size = rand() % fan_out + 1; - arg_value->tests = new ArgValue::Tests[arg_value->size]; + arg_value->size = rand() % fan_out + 1; + arg_value->tests = new ArgValue::Tests[arg_value->size]; - uint32_t k_value = rand(); + uint32_t k_value = rand(); for (int n = 0; n < arg_value->size; ++n) { // Ensure that we have unique values - k_value += rand() % (RAND_MAX/(kMaxFanOut+1)) + 1; + k_value += rand() % (RAND_MAX / (kMaxFanOut + 1)) + 1; // There are two possible types of nodes. Either this is a leaf node; // in that case, we have completed all the equality tests that we @@ -966,7 +986,7 @@ class EqualityStressTest { } else { arg_value->tests[n].err = 0; arg_value->tests[n].arg_value = - RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1); + RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1); } } // Finally, we have to define what we should return if none of the @@ -978,7 +998,7 @@ class EqualityStressTest { } else { arg_value->err = 0; arg_value->arg_value = - RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1); + RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1); } // We have now built a new (sub-)tree of ArgValues defining a set of // boolean expressions for testing random system call arguments against @@ -999,7 +1019,7 @@ class EqualityStressTest { return argno; } - void DeleteArgValue(ArgValue *arg_value) { + void DeleteArgValue(ArgValue* arg_value) { // Delete an ArgValue and all of its child nodes. This requires // recursively descending into the tree. if (arg_value) { @@ -1018,7 +1038,7 @@ class EqualityStressTest { } } - ErrorCode ToErrorCode(Sandbox *sandbox, ArgValue *arg_value) { + ErrorCode ToErrorCode(SandboxBPF* sandbox, ArgValue* arg_value) { // Compute the ErrorCode that should be returned, if none of our // tests succeed (i.e. the system call parameter doesn't match any // of the values in arg_value->tests[].k_value). @@ -1029,15 +1049,15 @@ class EqualityStressTest { err = ErrorCode(arg_value->err); } else { // If this wasn't a leaf node yet, recursively descend into the rest - // of the tree. This will end up adding a few more Sandbox::Cond() + // of the tree. This will end up adding a few more SandboxBPF::Cond() // tests to our ErrorCode. err = ToErrorCode(sandbox, arg_value->arg_value); } // Now, iterate over all the test cases that we want to compare against. - // This builds a chain of Sandbox::Cond() tests + // This builds a chain of SandboxBPF::Cond() tests // (aka "if ... elif ... elif ... elif ... fi") - for (int n = arg_value->size; n-- > 0; ) { + for (int n = arg_value->size; n-- > 0;) { ErrorCode matched; // Again, we distinguish between leaf nodes and subtrees. if (arg_value->tests[n].err) { @@ -1048,19 +1068,22 @@ class EqualityStressTest { // For now, all of our tests are limited to 32bit. // We have separate tests that check the behavior of 32bit vs. 64bit // conditional expressions. - err = sandbox->Cond(arg_value->argno, ErrorCode::TP_32BIT, - ErrorCode::OP_EQUAL, arg_value->tests[n].k_value, - matched, err); + err = sandbox->Cond(arg_value->argno, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + arg_value->tests[n].k_value, + matched, + err); } return err; } - void Verify(int sysno, intptr_t *args, const ArgValue& arg_value) { + void Verify(int sysno, intptr_t* args, const ArgValue& arg_value) { uint32_t mismatched = 0; // Iterate over all the k_values in arg_value.tests[] and verify that // we see the expected return values from system calls, when we pass // the k_value as a parameter in a system call. - for (int n = arg_value.size; n-- > 0; ) { + for (int n = arg_value.size; n-- > 0;) { mismatched += arg_value.tests[n].k_value; args[arg_value.argno] = arg_value.tests[n].k_value; if (arg_value.tests[n].err) { @@ -1069,12 +1092,12 @@ class EqualityStressTest { Verify(sysno, args, *arg_value.tests[n].arg_value); } } - // Find a k_value that doesn't match any of the k_values in - // arg_value.tests[]. In most cases, the current value of "mismatched" - // would fit this requirement. But on the off-chance that it happens - // to collide, we double-check. + // Find a k_value that doesn't match any of the k_values in + // arg_value.tests[]. In most cases, the current value of "mismatched" + // would fit this requirement. But on the off-chance that it happens + // to collide, we double-check. try_again: - for (int n = arg_value.size; n-- > 0; ) { + for (int n = arg_value.size; n-- > 0;) { if (mismatched == arg_value.tests[n].k_value) { ++mismatched; goto try_again; @@ -1094,61 +1117,75 @@ class EqualityStressTest { args[arg_value.argno] = 0; } - void VerifyErrno(int sysno, intptr_t *args, int err) { + void VerifyErrno(int sysno, intptr_t* args, int err) { // We installed BPF filters that return different errno values // based on the system call number and the parameters that we decided // to pass in. Verify that this condition holds true. - BPF_ASSERT(SandboxSyscall(sysno, - args[0], args[1], args[2], - args[3], args[4], args[5]) == -err); + BPF_ASSERT( + SandboxSyscall( + sysno, args[0], args[1], args[2], args[3], args[4], args[5]) == + -err); } // Vector of ArgValue trees. These trees define all the possible boolean // expressions that we want to turn into a BPF filter program. - std::vector arg_values_; + std::vector arg_values_; // Don't increase these values. We are pushing the limits of the maximum // BPF program that the kernel will allow us to load. If the values are // increased too much, the test will start failing. - static const int kNumIterations = 3; static const int kNumTestCases = 40; static const int kMaxFanOut = 3; static const int kMaxArgs = 6; }; -ErrorCode EqualityStressTestPolicy(Sandbox *sandbox, int sysno, void *aux) { - return reinterpret_cast(aux)->Policy(sandbox, sysno); +ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox, int sysno, void* aux) { + return reinterpret_cast(aux)->Policy(sandbox, sysno); } -BPF_TEST(SandboxBpf, EqualityTests, EqualityStressTestPolicy, +BPF_TEST(SandboxBPF, + EqualityTests, + EqualityStressTestPolicy, EqualityStressTest /* BPF_AUX */) { BPF_AUX.VerifyFilter(); } -ErrorCode EqualityArgumentWidthPolicy(Sandbox *sandbox, int sysno, void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode EqualityArgumentWidthPolicy(SandboxBPF* sandbox, int sysno, void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno == __NR_uname) { - return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0, - sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - 0x55555555, ErrorCode(1), ErrorCode(2)), - // The BPF compiler and the BPF interpreter in the kernel are - // (mostly) agnostic of the host platform's word size. The compiler - // will happily generate code that tests a 64bit value, and the - // interpreter will happily perform this test. - // But unless there is a kernel bug, there is no way for us to pass - // in a 64bit quantity on a 32bit platform. The upper 32bits should - // always be zero. So, this test should always evaluate as false on - // 32bit systems. - sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL, - 0x55555555AAAAAAAAULL, ErrorCode(1), ErrorCode(2))); + return sandbox->Cond( + 0, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + 0, + sandbox->Cond(1, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + 0x55555555, + ErrorCode(1), + ErrorCode(2)), + // The BPF compiler and the BPF interpreter in the kernel are + // (mostly) agnostic of the host platform's word size. The compiler + // will happily generate code that tests a 64bit value, and the + // interpreter will happily perform this test. + // But unless there is a kernel bug, there is no way for us to pass + // in a 64bit quantity on a 32bit platform. The upper 32bits should + // always be zero. So, this test should always evaluate as false on + // 32bit systems. + sandbox->Cond(1, + ErrorCode::TP_64BIT, + ErrorCode::OP_EQUAL, + 0x55555555AAAAAAAAULL, + ErrorCode(1), + ErrorCode(2))); } else { return ErrorCode(ErrorCode::ERR_ALLOWED); } } -BPF_TEST(SandboxBpf, EqualityArgumentWidth, EqualityArgumentWidthPolicy) { +BPF_TEST(SandboxBPF, EqualityArgumentWidth, EqualityArgumentWidthPolicy) { BPF_ASSERT(SandboxSyscall(__NR_uname, 0, 0x55555555) == -1); BPF_ASSERT(SandboxSyscall(__NR_uname, 0, 0xAAAAAAAA) == -2); #if __SIZEOF_POINTER__ > 4 @@ -1168,27 +1205,34 @@ BPF_TEST(SandboxBpf, EqualityArgumentWidth, EqualityArgumentWidthPolicy) { // On 32bit machines, there is no way to pass a 64bit argument through the // syscall interface. So, we have to skip the part of the test that requires // 64bit arguments. -BPF_DEATH_TEST(SandboxBpf, EqualityArgumentUnallowed64bit, +BPF_DEATH_TEST(SandboxBPF, + EqualityArgumentUnallowed64bit, DEATH_MESSAGE("Unexpected 64bit argument detected"), EqualityArgumentWidthPolicy) { SandboxSyscall(__NR_uname, 0, 0x5555555555555555ULL); } #endif -ErrorCode EqualityWithNegativeArgumentsPolicy(Sandbox *sandbox, int sysno, - void *) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode EqualityWithNegativeArgumentsPolicy(SandboxBPF* sandbox, + int sysno, + void*) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno == __NR_uname) { - return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - 0xFFFFFFFF, ErrorCode(1), ErrorCode(2)); + return sandbox->Cond(0, + ErrorCode::TP_32BIT, + ErrorCode::OP_EQUAL, + 0xFFFFFFFF, + ErrorCode(1), + ErrorCode(2)); } else { return ErrorCode(ErrorCode::ERR_ALLOWED); } } -BPF_TEST(SandboxBpf, EqualityWithNegativeArguments, +BPF_TEST(SandboxBPF, + EqualityWithNegativeArguments, EqualityWithNegativeArgumentsPolicy) { BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF) == -1); BPF_ASSERT(SandboxSyscall(__NR_uname, -1) == -1); @@ -1196,7 +1240,8 @@ BPF_TEST(SandboxBpf, EqualityWithNegativeArguments, } #if __SIZEOF_POINTER__ > 4 -BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments, +BPF_DEATH_TEST(SandboxBPF, + EqualityWithNegative64bitArguments, DEATH_MESSAGE("Unexpected 64bit argument detected"), EqualityWithNegativeArgumentsPolicy) { // When expecting a 32bit system call argument, we look at the MSB of the @@ -1205,15 +1250,14 @@ BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments, BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF00000000LL) == -1); } #endif - -ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) { +ErrorCode AllBitTestPolicy(SandboxBPF* sandbox, int sysno, void *) { // Test the OP_HAS_ALL_BITS conditional test operator with a couple of // different bitmasks. We try to find bitmasks that could conceivably // touch corner cases. // For all of these tests, we override the uname(). We can make use with // a single system call number, as we use the first system call argument to // select the different bit masks that we want to test against. - if (!Sandbox::IsValidSyscallNumber(sysno)) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno == __NR_uname) { @@ -1284,13 +1328,13 @@ ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) { // Most notably, "op" and "mask" are unused by the macro. If you want // to make changes to these values, you will have to edit the // test policy instead. -#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \ +#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \ BPF_ASSERT(SandboxSyscall(__NR_uname, (testcase), (arg)) == (expected_value)) // Our uname() system call returns ErrorCode(1) for success and // ErrorCode(0) for failure. SandboxSyscall() turns this into an // exit code of -1 or 0. -#define EXPECT_FAILURE 0 +#define EXPECT_FAILURE 0 #define EXPECT_SUCCESS -1 // A couple of our tests behave differently on 32bit and 64bit systems, as @@ -1298,10 +1342,8 @@ ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) { // argument "arg". // We expect these tests to succeed on 64bit systems, but to tail on 32bit // systems. -#define EXPT64_SUCCESS \ - (sizeof(void *) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE) - -BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) { +#define EXPT64_SUCCESS (sizeof(void*) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE) +BPF_TEST(SandboxBPF, AllBitTests, AllBitTestPolicy) { // 32bit test: all of 0x0 (should always be true) BITMASK_TEST( 0, 0, ALLBITS32, 0, EXPECT_SUCCESS); BITMASK_TEST( 0, 1, ALLBITS32, 0, EXPECT_SUCCESS); @@ -1404,14 +1446,14 @@ BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) { BITMASK_TEST(10, -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS); } -ErrorCode AnyBitTestPolicy(Sandbox *sandbox, int sysno, void *) { +ErrorCode AnyBitTestPolicy(SandboxBPF* sandbox, int sysno, void*) { // Test the OP_HAS_ANY_BITS conditional test operator with a couple of // different bitmasks. We try to find bitmasks that could conceivably // touch corner cases. // For all of these tests, we override the uname(). We can make use with // a single system call number, as we use the first system call argument to // select the different bit masks that we want to test against. - if (!Sandbox::IsValidSyscallNumber(sysno)) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno == __NR_uname) { @@ -1478,7 +1520,7 @@ ErrorCode AnyBitTestPolicy(Sandbox *sandbox, int sysno, void *) { } } -BPF_TEST(SandboxBpf, AnyBitTests, AnyBitTestPolicy) { +BPF_TEST(SandboxBPF, AnyBitTests, AnyBitTestPolicy) { // 32bit test: any of 0x0 (should always be false) BITMASK_TEST( 0, 0, ANYBITS32, 0x0, EXPECT_FAILURE); BITMASK_TEST( 0, 1, ANYBITS32, 0x0, EXPECT_FAILURE); @@ -1581,36 +1623,39 @@ BPF_TEST(SandboxBpf, AnyBitTests, AnyBitTestPolicy) { BITMASK_TEST( 10, -1L, ANYBITS64,0x100000001, EXPECT_SUCCESS); } -intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void *aux) { - if (args.args[0] != (CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD)) { +intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void* aux) { + if (args.args[0] != (CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD)) { // We expect to get called for an attempt to fork(). No need to log that // call. But if we ever get called for anything else, we want to verbosely // print as much information as possible. - const char *msg = (const char *)aux; - printf("Clone() was called with unexpected arguments\n" - " nr: %d\n" - " 1: 0x%llX\n" - " 2: 0x%llX\n" - " 3: 0x%llX\n" - " 4: 0x%llX\n" - " 5: 0x%llX\n" - " 6: 0x%llX\n" - "%s\n", - args.nr, - (long long)args.args[0], (long long)args.args[1], - (long long)args.args[2], (long long)args.args[3], - (long long)args.args[4], (long long)args.args[5], - msg); + const char* msg = (const char*)aux; + printf( + "Clone() was called with unexpected arguments\n" + " nr: %d\n" + " 1: 0x%llX\n" + " 2: 0x%llX\n" + " 3: 0x%llX\n" + " 4: 0x%llX\n" + " 5: 0x%llX\n" + " 6: 0x%llX\n" + "%s\n", + args.nr, + (long long)args.args[0], + (long long)args.args[1], + (long long)args.args[2], + (long long)args.args[3], + (long long)args.args[4], + (long long)args.args[5], + msg); } return -EPERM; } - -ErrorCode PthreadPolicyEquality(Sandbox *sandbox, int sysno, void *aux) { +ErrorCode PthreadPolicyEquality(SandboxBPF* sandbox, int sysno, void* aux) { // This policy allows creating threads with pthread_create(). But it // doesn't allow any other uses of clone(). Most notably, it does not // allow callers to implement fork() or vfork() by passing suitable flags // to the clone() system call. - if (!Sandbox::IsValidSyscallNumber(sysno)) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno == __NR_clone) { @@ -1618,30 +1663,39 @@ ErrorCode PthreadPolicyEquality(Sandbox *sandbox, int sysno, void *aux) { // uses the more modern flags, sets the TLS from the call to clone(), and // uses futexes to monitor threads. Android's C run-time library, doesn't // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED. + // More recent versions of Android don't set CLONE_DETACHED anymore, so + // the last case accounts for that. // The following policy is very strict. It only allows the exact masks // that we have seen in known implementations. It is probably somewhat // stricter than what we would want to do. + const uint64_t kGlibcCloneMask = + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | + CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; + const uint64_t kBaseAndroidCloneMask = + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_THREAD | CLONE_SYSVSEM; return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND| - CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS| - CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, + kGlibcCloneMask, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND| - CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED, + kBaseAndroidCloneMask | CLONE_DETACHED, ErrorCode(ErrorCode::ERR_ALLOWED), - sandbox->Trap(PthreadTrapHandler, aux))); + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + kBaseAndroidCloneMask, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Trap(PthreadTrapHandler, "Unknown mask")))); } else { return ErrorCode(ErrorCode::ERR_ALLOWED); } } -ErrorCode PthreadPolicyBitMask(Sandbox *sandbox, int sysno, void *aux) { +ErrorCode PthreadPolicyBitMask(SandboxBPF* sandbox, int sysno, void* aux) { // This policy allows creating threads with pthread_create(). But it // doesn't allow any other uses of clone(). Most notably, it does not // allow callers to implement fork() or vfork() by passing suitable flags // to the clone() system call. - if (!Sandbox::IsValidSyscallNumber(sysno)) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); } else if (sysno == __NR_clone) { @@ -1681,8 +1735,8 @@ ErrorCode PthreadPolicyBitMask(Sandbox *sandbox, int sysno, void *aux) { } } -static void *ThreadFnc(void *arg) { - ++*reinterpret_cast(arg); +static void* ThreadFnc(void* arg) { + ++*reinterpret_cast(arg); SandboxSyscall(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0); return NULL; } @@ -1702,8 +1756,8 @@ static void PthreadTest() { BPF_ASSERT(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)); BPF_ASSERT(!pthread_create(&thread, &attr, ThreadFnc, &thread_ran)); BPF_ASSERT(!pthread_attr_destroy(&attr)); - while (SandboxSyscall(__NR_futex, &thread_ran, FUTEX_WAIT, - 0, 0, 0, 0) == -EINTR) { + while (SandboxSyscall(__NR_futex, &thread_ran, FUTEX_WAIT, 0, 0, 0, 0) == + -EINTR) { } BPF_ASSERT(thread_ran); @@ -1714,16 +1768,16 @@ static void PthreadTest() { // __NR_clone, and that would introduce a bogus test failure. int pid; BPF_ASSERT(SandboxSyscall(__NR_clone, - CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, - 0, 0, &pid) == -EPERM); + CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD, + 0, + 0, + &pid) == -EPERM); } -BPF_TEST(SandboxBpf, PthreadEquality, PthreadPolicyEquality) { - PthreadTest(); -} +BPF_TEST(SandboxBPF, PthreadEquality, PthreadPolicyEquality) { PthreadTest(); } -BPF_TEST(SandboxBpf, PthreadBitMask, PthreadPolicyBitMask) { - PthreadTest(); -} +BPF_TEST(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) { PthreadTest(); } + +} // namespace -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.cc b/chromium/sandbox/linux/seccomp-bpf/syscall.cc index 8b09a684866..acf207dc14c 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall.cc +++ b/chromium/sandbox/linux/seccomp-bpf/syscall.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "sandbox/linux/seccomp-bpf/syscall.h" + #include #include -#include "sandbox/linux/seccomp-bpf/port.h" -#include "sandbox/linux/seccomp-bpf/syscall.h" - +#include "base/basictypes.h" -namespace playground2 { +namespace sandbox { asm( // We need to be able to tell the kernel exactly where we made a // system call. The C++ compiler likes to sometimes clone or @@ -196,7 +196,7 @@ intptr_t SandboxSyscall(int nr, // N.B. These are not the calling conventions normally used by the ABI. : "=a"(ret) : "0"(ret), "D"(args) - : "esp", "memory", "ecx", "edx"); + : "cc", "esp", "memory", "ecx", "edx"); #elif defined(__x86_64__) intptr_t ret = nr; { @@ -208,7 +208,7 @@ intptr_t SandboxSyscall(int nr, // N.B. These are not the calling conventions normally used by the ABI. : "=a"(ret) : "0"(ret), "r"(data) - : "rsp", "memory", + : "cc", "rsp", "memory", "rcx", "rdi", "rsi", "rdx", "r8", "r9", "r10", "r11"); } #elif defined(__arm__) @@ -221,7 +221,7 @@ intptr_t SandboxSyscall(int nr, // N.B. These are not the calling conventions normally used by the ABI. : "=r"(inout) : "0"(inout), "r"(data) - : "lr", "memory", "r1", "r2", "r3", "r4", "r5" + : "cc", "lr", "memory", "r1", "r2", "r3", "r4", "r5" #if !defined(__arm__) // In thumb mode, we cannot use "r7" as a general purpose register, as // it is our frame pointer. We have to manually manage and preserve it. @@ -240,4 +240,4 @@ intptr_t SandboxSyscall(int nr, return ret; } -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.h b/chromium/sandbox/linux/seccomp-bpf/syscall.h index 39b1bcaa4a5..0b51380e88b 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall.h +++ b/chromium/sandbox/linux/seccomp-bpf/syscall.h @@ -7,7 +7,7 @@ #include -namespace playground2 { +namespace sandbox { // We have to make sure that we have a single "magic" return address for // our system calls, which we can check from within a BPF filter. This @@ -16,9 +16,12 @@ namespace playground2 { // Passing "nr" as "-1" computes the "magic" return address. Passing any // other value invokes the appropriate system call. intptr_t SandboxSyscall(int nr, - intptr_t p0, intptr_t p1, intptr_t p2, - intptr_t p3, intptr_t p4, intptr_t p5); - + intptr_t p0, + intptr_t p1, + intptr_t p2, + intptr_t p3, + intptr_t p4, + intptr_t p5); // System calls can take up to six parameters. Traditionally, glibc // implements this property by using variadic argument lists. This works, but @@ -37,19 +40,30 @@ intptr_t SandboxSyscall(int nr, // easier to read as it hides implementation details. #if __cplusplus >= 201103 // C++11 -template -inline intptr_t SandboxSyscall(int nr, - T0 p0 = 0, T1 p1 = 0, T2 p2 = 0, - T3 p3 = 0, T4 p4 = 0, T5 p5 = 0) - __attribute__((always_inline)); - -template +template inline intptr_t SandboxSyscall(int nr, - T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + T0 p0 = 0, + T1 p1 = 0, + T2 p2 = 0, + T3 p3 = 0, + T4 p4 = 0, + T5 p5 = 0) __attribute__((always_inline)); + +template +inline intptr_t +SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { return SandboxSyscall(nr, - (intptr_t)p0, (intptr_t)p1, (intptr_t)p2, - (intptr_t)p3, (intptr_t)p4, (intptr_t)p5); + (intptr_t)p0, + (intptr_t)p1, + (intptr_t)p2, + (intptr_t)p3, + (intptr_t)p4, + (intptr_t)p5); } #else // Pre-C++11 @@ -58,66 +72,67 @@ inline intptr_t SandboxSyscall(int nr, // expressing what we are doing here. Delete the fall-back code for older // compilers as soon as we have fully switched to C++11 -template -inline intptr_t SandboxSyscall(int nr, - T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) - __attribute__((always_inline)); -template -inline intptr_t SandboxSyscall(int nr, - T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { +template +inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) + __attribute__((always_inline)); +template +inline intptr_t +SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { return SandboxSyscall(nr, - (intptr_t)p0, (intptr_t)p1, (intptr_t)p2, - (intptr_t)p3, (intptr_t)p4, (intptr_t)p5); + (intptr_t)p0, + (intptr_t)p1, + (intptr_t)p2, + (intptr_t)p3, + (intptr_t)p4, + (intptr_t)p5); } -template +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) - __attribute__((always_inline)); -template + __attribute__((always_inline)); +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) { return SandboxSyscall(nr, p0, p1, p2, p3, p4, 0); } -template +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3) - __attribute__((always_inline)); -template + __attribute__((always_inline)); +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3) { return SandboxSyscall(nr, p0, p1, p2, p3, 0, 0); } -template +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2) - __attribute__((always_inline)); -template + __attribute__((always_inline)); +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2) { return SandboxSyscall(nr, p0, p1, p2, 0, 0, 0); } -template +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1) - __attribute__((always_inline)); -template + __attribute__((always_inline)); +template inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1) { return SandboxSyscall(nr, p0, p1, 0, 0, 0, 0); } -template -inline intptr_t SandboxSyscall(int nr, T0 p0) - __attribute__((always_inline)); -template +template +inline intptr_t SandboxSyscall(int nr, T0 p0) __attribute__((always_inline)); +template inline intptr_t SandboxSyscall(int nr, T0 p0) { return SandboxSyscall(nr, p0, 0, 0, 0, 0, 0); } -inline intptr_t SandboxSyscall(int nr) - __attribute__((always_inline)); +inline intptr_t SandboxSyscall(int nr) __attribute__((always_inline)); inline intptr_t SandboxSyscall(int nr) { return SandboxSyscall(nr, 0, 0, 0, 0, 0, 0); } #endif // Pre-C++11 -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc index 4ea979a63f3..89cc1cb473c 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc +++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/seccomp-bpf/port.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" -namespace playground2 { +#include "base/basictypes.h" +#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" + +namespace sandbox { uint32_t SyscallIterator::Next() { if (done_) { @@ -17,8 +18,7 @@ uint32_t SyscallIterator::Next() { do { // |num_| has been initialized to 0, which we assume is also MIN_SYSCALL. // This true for supported architectures (Intel and ARM EABI). - COMPILE_ASSERT(MIN_SYSCALL == 0u, - min_syscall_should_always_be_zero); + COMPILE_ASSERT(MIN_SYSCALL == 0u, min_syscall_should_always_be_zero); val = num_; // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL @@ -30,9 +30,9 @@ uint32_t SyscallIterator::Next() { ++num_; } #if defined(__arm__) - // ARM EABI includes "ARM private" system calls starting at - // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at - // MIN_GHOST_SYSCALL. + // ARM EABI includes "ARM private" system calls starting at + // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at + // MIN_GHOST_SYSCALL. } else if (num_ < MIN_PRIVATE_SYSCALL - 1) { num_ = MIN_PRIVATE_SYSCALL - 1; } else if (num_ <= MAX_PRIVATE_SYSCALL) { @@ -50,12 +50,12 @@ uint32_t SyscallIterator::Next() { ++num_; } #endif - // BPF programs only ever operate on unsigned quantities. So, that's how - // we iterate; we return values from 0..0xFFFFFFFFu. But there are places, - // where the kernel might interpret system call numbers as signed - // quantities, so the boundaries between signed and unsigned values are - // potential problem cases. We want to explicitly return these values from - // our iterator. + // BPF programs only ever operate on unsigned quantities. So, that's how + // we iterate; we return values from 0..0xFFFFFFFFu. But there are places, + // where the kernel might interpret system call numbers as signed + // quantities, so the boundaries between signed and unsigned values are + // potential problem cases. We want to explicitly return these values from + // our iterator. } else if (num_ < 0x7FFFFFFFu) { num_ = 0x7FFFFFFFu; } else if (num_ < 0x80000000u) { @@ -86,10 +86,7 @@ bool SyscallIterator::IsArmPrivate(uint32_t num) { (num >= MIN_GHOST_SYSCALL && num <= MAX_SYSCALL); } #else -bool SyscallIterator::IsArmPrivate(uint32_t) { - return false; -} +bool SyscallIterator::IsArmPrivate(uint32_t) { return false; } #endif -} // namespace - +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h index e17593d844f..3b56ea3144d 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h +++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h @@ -7,7 +7,9 @@ #include -namespace playground2 { +#include "base/basictypes.h" + +namespace sandbox { // Iterates over the entire system call range from 0..0xFFFFFFFFu. This // iterator is aware of how system calls look like and will skip quickly @@ -32,9 +34,7 @@ namespace playground2 { class SyscallIterator { public: explicit SyscallIterator(bool invalid_only) - : invalid_only_(invalid_only), - done_(false), - num_(0) {} + : invalid_only_(invalid_only), done_(false), num_(0) {} bool Done() const { return done_; } uint32_t Next(); @@ -43,14 +43,13 @@ class SyscallIterator { private: static bool IsArmPrivate(uint32_t num); - bool invalid_only_; - bool done_; + bool invalid_only_; + bool done_; uint32_t num_; DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallIterator); }; -} // namespace playground2 +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__ - diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc index 26f11ce5e34..08a857a2e29 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc @@ -6,13 +6,13 @@ #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/tests/unit_tests.h" -using namespace playground2; +namespace sandbox { namespace { SANDBOX_TEST(SyscallIterator, Monotonous) { for (int i = 0; i < 2; ++i) { - bool invalid_only = !i; // Testing both |invalid_only| cases. + bool invalid_only = !i; // Testing both |invalid_only| cases. SyscallIterator iter(invalid_only); uint32_t next = iter.Next(); @@ -79,7 +79,7 @@ SANDBOX_TEST(SyscallIterator, ARMHiddenSyscallRange) { SANDBOX_TEST(SyscallIterator, Invalid) { for (int i = 0; i < 2; ++i) { - bool invalid_only = !i; // Testing both |invalid_only| cases. + bool invalid_only = !i; // Testing both |invalid_only| cases. SyscallIterator iter(invalid_only); uint32_t next = iter.Next(); @@ -133,3 +133,4 @@ SANDBOX_TEST(SyscallIterator, InvalidOnly) { } // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc index 136deb615cb..60db69bcd6b 100644 --- a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc @@ -18,7 +18,7 @@ #include "sandbox/linux/tests/unit_tests.h" #include "testing/gtest/include/gtest/gtest.h" -using namespace playground2; +namespace sandbox { namespace { @@ -31,27 +31,27 @@ const int kMMapNr = __NR_mmap; #endif TEST(Syscall, WellKnownEntryPoint) { - // Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM, - // where syscall(-1) crashes with SIGILL. Not running the test is fine, as we - // are still testing ARM code in the next set of tests. +// Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM, +// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we +// are still testing ARM code in the next set of tests. #if !defined(__arm__) EXPECT_NE(SandboxSyscall(-1), syscall(-1)); #endif - // If possible, test that SandboxSyscall(-1) returns the address right after - // a kernel entry point. +// If possible, test that SandboxSyscall(-1) returns the address right after +// a kernel entry point. #if defined(__i386__) - EXPECT_EQ(0x80CDu, ((uint16_t *)SandboxSyscall(-1))[-1]); // INT 0x80 + EXPECT_EQ(0x80CDu, ((uint16_t*)SandboxSyscall(-1))[-1]); // INT 0x80 #elif defined(__x86_64__) - EXPECT_EQ(0x050Fu, ((uint16_t *)SandboxSyscall(-1))[-1]); // SYSCALL + EXPECT_EQ(0x050Fu, ((uint16_t*)SandboxSyscall(-1))[-1]); // SYSCALL #elif defined(__arm__) #if defined(__thumb__) - EXPECT_EQ(0xDF00u, ((uint16_t *)SandboxSyscall(-1))[-1]); // SWI 0 + EXPECT_EQ(0xDF00u, ((uint16_t*)SandboxSyscall(-1))[-1]); // SWI 0 #else - EXPECT_EQ(0xEF000000u, ((uint32_t *)SandboxSyscall(-1))[-1]); // SVC 0 + EXPECT_EQ(0xEF000000u, ((uint32_t*)SandboxSyscall(-1))[-1]); // SVC 0 #endif #else - #warning Incomplete test case; need port for target platform +#warning Incomplete test case; need port for target platform #endif } @@ -64,12 +64,12 @@ TEST(Syscall, TrivialSyscallOneArg) { int new_fd; // Duplicate standard error and close it. ASSERT_GE(new_fd = SandboxSyscall(__NR_dup, 2), 0); - int close_return_value = HANDLE_EINTR(SandboxSyscall(__NR_close, new_fd)); + int close_return_value = IGNORE_EINTR(SandboxSyscall(__NR_close, new_fd)); ASSERT_EQ(close_return_value, 0); } // SIGSYS trap handler that will be called on __NR_uname. -intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void *aux) { +intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) { // |aux| is a pointer to our BPF_AUX. std::vector* const seen_syscall_args = static_cast*>(aux); @@ -78,8 +78,8 @@ intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void *aux) { return -ENOMEM; } -ErrorCode CopyAllArgsOnUnamePolicy(Sandbox *sandbox, int sysno, void *aux) { - if (!Sandbox::IsValidSyscallNumber(sysno)) { +ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, int sysno, void* aux) { + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } if (sysno == __NR_uname) { @@ -91,7 +91,9 @@ ErrorCode CopyAllArgsOnUnamePolicy(Sandbox *sandbox, int sysno, void *aux) { // We are testing SandboxSyscall() by making use of a BPF filter that allows us // to inspect the system call arguments that the kernel saw. -BPF_TEST(Syscall, SyntheticSixArgs, CopyAllArgsOnUnamePolicy, +BPF_TEST(Syscall, + SyntheticSixArgs, + CopyAllArgsOnUnamePolicy, std::vector /* BPF_AUX */) { const int kExpectedValue = 42; // In this test we only pass integers to the kernel. We might want to make @@ -105,12 +107,13 @@ BPF_TEST(Syscall, SyntheticSixArgs, CopyAllArgsOnUnamePolicy, // We could use pretty much any system call we don't need here. uname() is // nice because it doesn't have any dangerous side effects. - BPF_ASSERT(SandboxSyscall(__NR_uname, syscall_args[0], - syscall_args[1], - syscall_args[2], - syscall_args[3], - syscall_args[4], - syscall_args[5]) == -ENOMEM); + BPF_ASSERT(SandboxSyscall(__NR_uname, + syscall_args[0], + syscall_args[1], + syscall_args[2], + syscall_args[3], + syscall_args[4], + syscall_args[5]) == -ENOMEM); // We expect the trap handler to have copied the 6 arguments. BPF_ASSERT(BPF_AUX.size() == 6); @@ -131,43 +134,54 @@ TEST(Syscall, ComplexSyscallSixArgs) { ASSERT_LE(0, fd = SandboxSyscall(__NR_open, "/dev/null", O_RDWR, 0L)); // Use mmap() to allocate some read-only memory - char *addr0; - ASSERT_NE((char *)NULL, - addr0 = reinterpret_cast( - SandboxSyscall(kMMapNr, (void *)NULL, 4096, PROT_READ, - MAP_PRIVATE|MAP_ANONYMOUS, fd, 0L))); + char* addr0; + ASSERT_NE((char*)NULL, + addr0 = reinterpret_cast( + SandboxSyscall(kMMapNr, + (void*)NULL, + 4096, + PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, + fd, + 0L))); // Try to replace the existing mapping with a read-write mapping - char *addr1; + char* addr1; ASSERT_EQ(addr0, - addr1 = reinterpret_cast( - SandboxSyscall(kMMapNr, addr0, 4096L, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, - fd, 0L))); - ++*addr1; // This should not seg fault + addr1 = reinterpret_cast( + SandboxSyscall(kMMapNr, + addr0, + 4096L, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + fd, + 0L))); + ++*addr1; // This should not seg fault // Clean up EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr1, 4096L)); - EXPECT_EQ(0, HANDLE_EINTR(SandboxSyscall(__NR_close, fd))); + EXPECT_EQ(0, IGNORE_EINTR(SandboxSyscall(__NR_close, fd))); // Check that the offset argument (i.e. the sixth argument) is processed // correctly. ASSERT_GE(fd = SandboxSyscall(__NR_open, "/proc/self/exe", O_RDONLY, 0L), 0); - char *addr2, *addr3; - ASSERT_NE((char *)NULL, - addr2 = reinterpret_cast( - SandboxSyscall(kMMapNr, (void *)NULL, 8192L, PROT_READ, - MAP_PRIVATE, fd, 0L))); - ASSERT_NE((char *)NULL, - addr3 = reinterpret_cast( - SandboxSyscall(kMMapNr, (void *)NULL, 4096L, PROT_READ, - MAP_PRIVATE, fd, + char* addr2, *addr3; + ASSERT_NE((char*)NULL, + addr2 = reinterpret_cast(SandboxSyscall( + kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L))); + ASSERT_NE((char*)NULL, + addr3 = reinterpret_cast(SandboxSyscall(kMMapNr, + (void*)NULL, + 4096L, + PROT_READ, + MAP_PRIVATE, + fd, #if defined(__NR_mmap2) - 1L + 1L #else - 4096L + 4096L #endif - ))); + ))); EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096)); // Just to be absolutely on the safe side, also verify that the file @@ -179,7 +193,9 @@ TEST(Syscall, ComplexSyscallSixArgs) { // Clean up EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr2, 8192L)); EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr3, 4096L)); - EXPECT_EQ(0, HANDLE_EINTR(SandboxSyscall(__NR_close, fd))); + EXPECT_EQ(0, IGNORE_EINTR(SandboxSyscall(__NR_close, fd))); } -} // namespace +} // namespace + +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.cc b/chromium/sandbox/linux/seccomp-bpf/trap.cc index 499c81b535b..553a9043bfb 100644 --- a/chromium/sandbox/linux/seccomp-bpf/trap.cc +++ b/chromium/sandbox/linux/seccomp-bpf/trap.cc @@ -2,30 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "sandbox/linux/seccomp-bpf/trap.h" + #include #include #include #include #include -#ifndef SECCOMP_BPF_STANDALONE -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#endif +#include +#include "base/logging.h" #include "sandbox/linux/seccomp-bpf/codegen.h" #include "sandbox/linux/seccomp-bpf/die.h" #include "sandbox/linux/seccomp-bpf/syscall.h" -#include "sandbox/linux/seccomp-bpf/trap.h" // Android's signal.h doesn't define ucontext etc. #if defined(OS_ANDROID) #include "sandbox/linux/services/android_ucontext.h" #endif -#include - - namespace { const int kCapacityIncrement = 20; @@ -47,23 +43,21 @@ const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING"; // realtime signals. There are plenty of them. Unfortunately, there is no // way to mark a signal as allocated. So, the potential for collision is // possibly even worse. -bool GetIsInSigHandler(const ucontext_t *ctx) { +bool GetIsInSigHandler(const ucontext_t* ctx) { // Note: on Android, sigismember does not take a pointer to const. return sigismember(const_cast(&ctx->uc_sigmask), SIGBUS); } void SetIsInSigHandler() { sigset_t mask; - if (sigemptyset(&mask) || - sigaddset(&mask, SIGBUS) || + if (sigemptyset(&mask) || sigaddset(&mask, SIGBUS) || sigprocmask(SIG_BLOCK, &mask, NULL)) { SANDBOX_DIE("Failed to block SIGBUS"); } } bool IsDefaultSignalAction(const struct sigaction& sa) { - if (sa.sa_flags & SA_SIGINFO || - sa.sa_handler != SIG_DFL) { + if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) { return false; } return true; @@ -71,7 +65,7 @@ bool IsDefaultSignalAction(const struct sigaction& sa) { } // namespace -namespace playground2 { +namespace sandbox { Trap::Trap() : trap_array_(NULL), @@ -79,7 +73,7 @@ Trap::Trap() trap_array_capacity_(0), has_unsafe_traps_(false) { // Set new SIGSYS handler - struct sigaction sa = { }; + struct sigaction sa = {}; sa.sa_sigaction = SigSysAction; sa.sa_flags = SA_SIGINFO | SA_NODEFER; struct sigaction old_sa; @@ -94,14 +88,13 @@ Trap::Trap() // Unmask SIGSYS sigset_t mask; - if (sigemptyset(&mask) || - sigaddset(&mask, SIGSYS) || + if (sigemptyset(&mask) || sigaddset(&mask, SIGSYS) || sigprocmask(SIG_UNBLOCK, &mask, NULL)) { SANDBOX_DIE("Failed to configure SIGSYS handler"); } } -Trap *Trap::GetInstance() { +Trap* Trap::GetInstance() { // Note: This class is not thread safe. It is the caller's responsibility // to avoid race conditions. Normally, this is a non-issue as the sandbox // can only be initialized if there are no other threads present. @@ -116,15 +109,16 @@ Trap *Trap::GetInstance() { return global_trap_; } -void Trap::SigSysAction(int nr, siginfo_t *info, void *void_context) { +void Trap::SigSysAction(int nr, siginfo_t* info, void* void_context) { if (!global_trap_) { - RAW_SANDBOX_DIE("This can't happen. Found no global singleton instance " - "for Trap() handling."); + RAW_SANDBOX_DIE( + "This can't happen. Found no global singleton instance " + "for Trap() handling."); } global_trap_->SigSys(nr, info, void_context); } -void Trap::SigSys(int nr, siginfo_t *info, void *void_context) { +void Trap::SigSys(int nr, siginfo_t* info, void* void_context) { // Signal handlers should always preserve "errno". Otherwise, we could // trigger really subtle bugs. const int old_errno = errno; @@ -145,7 +139,7 @@ void Trap::SigSys(int nr, siginfo_t *info, void *void_context) { // Obtain the signal context. This, most notably, gives us access to // all CPU registers at the time of the signal. - ucontext_t *ctx = reinterpret_cast(void_context); + ucontext_t* ctx = reinterpret_cast(void_context); // Obtain the siginfo information that is specific to SIGSYS. Unfortunately, // most versions of glibc don't include this information in siginfo_t. So, @@ -154,7 +148,7 @@ void Trap::SigSys(int nr, siginfo_t *info, void *void_context) { memcpy(&sigsys, &info->_sifields, sizeof(sigsys)); // Some more sanity checks. - if (sigsys.ip != reinterpret_cast(SECCOMP_IP(ctx)) || + if (sigsys.ip != reinterpret_cast(SECCOMP_IP(ctx)) || sigsys.nr != static_cast(SECCOMP_SYSCALL(ctx)) || sigsys.arch != SECCOMP_ARCH) { // TODO(markus): @@ -172,9 +166,12 @@ void Trap::SigSys(int nr, siginfo_t *info, void *void_context) { RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler."); } rc = SandboxSyscall(sigsys.nr, - SECCOMP_PARM1(ctx), SECCOMP_PARM2(ctx), - SECCOMP_PARM3(ctx), SECCOMP_PARM4(ctx), - SECCOMP_PARM5(ctx), SECCOMP_PARM6(ctx)); + SECCOMP_PARM1(ctx), + SECCOMP_PARM2(ctx), + SECCOMP_PARM3(ctx), + SECCOMP_PARM4(ctx), + SECCOMP_PARM5(ctx), + SECCOMP_PARM6(ctx)); } else { const ErrorCode& err = trap_array_[info->si_errno - 1]; if (!err.safe_) { @@ -185,18 +182,13 @@ void Trap::SigSys(int nr, siginfo_t *info, void *void_context) { // is what we are showing to TrapFnc callbacks that the system call // evaluator registered with the sandbox. struct arch_seccomp_data data = { - sigsys.nr, - SECCOMP_ARCH, - reinterpret_cast(sigsys.ip), - { - static_cast(SECCOMP_PARM1(ctx)), - static_cast(SECCOMP_PARM2(ctx)), - static_cast(SECCOMP_PARM3(ctx)), - static_cast(SECCOMP_PARM4(ctx)), - static_cast(SECCOMP_PARM5(ctx)), - static_cast(SECCOMP_PARM6(ctx)) - } - }; + sigsys.nr, SECCOMP_ARCH, reinterpret_cast(sigsys.ip), + {static_cast(SECCOMP_PARM1(ctx)), + static_cast(SECCOMP_PARM2(ctx)), + static_cast(SECCOMP_PARM3(ctx)), + static_cast(SECCOMP_PARM4(ctx)), + static_cast(SECCOMP_PARM5(ctx)), + static_cast(SECCOMP_PARM6(ctx))}}; // Now call the TrapFnc callback associated with this particular instance // of SECCOMP_RET_TRAP. @@ -207,7 +199,7 @@ void Trap::SigSys(int nr, siginfo_t *info, void *void_context) { // that we just handled, and restore "errno" to the value that it had // before entering the signal handler. SECCOMP_RESULT(ctx) = static_cast(rc); - errno = old_errno; + errno = old_errno; return; } @@ -222,11 +214,11 @@ bool Trap::TrapKey::operator<(const TrapKey& o) const { } } -ErrorCode Trap::MakeTrap(TrapFnc fnc, const void *aux, bool safe) { +ErrorCode Trap::MakeTrap(TrapFnc fnc, const void* aux, bool safe) { return GetInstance()->MakeTrapImpl(fnc, aux, safe); } -ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) { +ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe) { if (!safe && !SandboxDebuggingAllowedByUser()) { // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable, // we never return an ErrorCode that is marked as "unsafe". This also @@ -239,8 +231,9 @@ ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) { // to understand. Removing the SANDBOX_DIE() allows callers to easyly check // whether unsafe traps are supported (by checking whether the returned // ErrorCode is ET_INVALID). - SANDBOX_DIE("Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING " - "is enabled"); + SANDBOX_DIE( + "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING " + "is enabled"); return ErrorCode(); } @@ -290,9 +283,9 @@ ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) { // against issues with the memory model or with completely asynchronous // events. if (trap_array_size_ >= trap_array_capacity_) { - trap_array_capacity_ += kCapacityIncrement; - ErrorCode *old_trap_array = trap_array_; - ErrorCode *new_trap_array = new ErrorCode[trap_array_capacity_]; + trap_array_capacity_ += kCapacityIncrement; + ErrorCode* old_trap_array = trap_array_; + ErrorCode* new_trap_array = new ErrorCode[trap_array_capacity_]; // Language specs are unclear on whether the compiler is allowed to move // the "delete[]" above our preceding assignments and/or memory moves, @@ -305,7 +298,7 @@ ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) { // legitimate worry; but they at least thought that the barrier is // sufficient to prevent the (so far hypothetical) problem of re-ordering // of instructions by the compiler. - memcpy(new_trap_array, trap_array_, trap_array_size_*sizeof(ErrorCode)); + memcpy(new_trap_array, trap_array_, trap_array_size_ * sizeof(ErrorCode)); asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory"); trap_array_ = new_trap_array; asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory"); @@ -321,13 +314,12 @@ ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) { } bool Trap::SandboxDebuggingAllowedByUser() const { - const char *debug_flag = getenv(kSandboxDebuggingEnv); + const char* debug_flag = getenv(kSandboxDebuggingEnv); return debug_flag && *debug_flag; } - bool Trap::EnableUnsafeTrapsInSigSysHandler() { - Trap *trap = GetInstance(); + Trap* trap = GetInstance(); if (!trap->has_unsafe_traps_) { // Unsafe traps are a one-way fuse. Once enabled, they can never be turned // off again. @@ -340,8 +332,9 @@ bool Trap::EnableUnsafeTrapsInSigSysHandler() { SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes"); trap->has_unsafe_traps_ = true; } else { - SANDBOX_INFO("Cannot disable sandbox and use unsafe traps unless " - "CHROME_SANDBOX_DEBUGGING is turned on first"); + SANDBOX_INFO( + "Cannot disable sandbox and use unsafe traps unless " + "CHROME_SANDBOX_DEBUGGING is turned on first"); } } // Returns the, possibly updated, value of has_unsafe_traps_. @@ -356,6 +349,6 @@ ErrorCode Trap::ErrorCodeFromTrapId(uint16_t id) { } } -Trap *Trap::global_trap_; +Trap* Trap::global_trap_; -} // namespace playground2 +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.h b/chromium/sandbox/linux/seccomp-bpf/trap.h index 2a4c6ed7137..334a30d965a 100644 --- a/chromium/sandbox/linux/seccomp-bpf/trap.h +++ b/chromium/sandbox/linux/seccomp-bpf/trap.h @@ -11,10 +11,9 @@ #include #include -#include "sandbox/linux/seccomp-bpf/port.h" +#include "base/basictypes.h" - -namespace playground2 { +namespace sandbox { class ErrorCode; @@ -41,13 +40,13 @@ class Trap { // range -1..-4096. It should not set errno when reporting errors; on the // other hand, accidentally modifying errno is harmless and the changes will // be undone afterwards. - typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux); + typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void* aux); // Registers a new trap handler and sets up the appropriate SIGSYS handler // as needed. // N.B.: This makes a permanent state change. Traps cannot be unregistered, // as that would break existing BPF filters that are still active. - static ErrorCode MakeTrap(TrapFnc fnc, const void *aux, bool safe); + static ErrorCode MakeTrap(TrapFnc fnc, const void* aux, bool safe); // Enables support for unsafe traps in the SIGSYS signal handler. This is a // one-way fuse. It works in conjunction with the BPF compiler emitting code @@ -68,14 +67,10 @@ class Trap { ~Trap(); struct TrapKey { - TrapKey(TrapFnc f, const void *a, bool s) - : fnc(f), - aux(a), - safe(s) { - } - TrapFnc fnc; - const void *aux; - bool safe; + TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {} + TrapFnc fnc; + const void* aux; + bool safe; bool operator<(const TrapKey&) const; }; typedef std::map TrapIds; @@ -87,29 +82,27 @@ class Trap { // It also gracefully deals with methods that should check for the singleton, // but avoid instantiating it, if it doesn't exist yet // (e.g. ErrorCodeFromTrapId()). - static Trap *GetInstance(); - static void SigSysAction(int nr, siginfo_t *info, void *void_context); + static Trap* GetInstance(); + static void SigSysAction(int nr, siginfo_t* info, void* void_context); // Make sure that SigSys is not inlined in order to get slightly better crash // dumps. - void SigSys(int nr, siginfo_t *info, void *void_context) - __attribute__ ((noinline)); - ErrorCode MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe); + void SigSys(int nr, siginfo_t* info, void* void_context) + __attribute__((noinline)); + ErrorCode MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe); bool SandboxDebuggingAllowedByUser() const; - - // We have a global singleton that handles all of our SIGSYS traps. This // variable must never be deallocated after it has been set up initially, as // there is no way to reset in-kernel BPF filters that generate SIGSYS // events. - static Trap *global_trap_; + static Trap* global_trap_; - TrapIds trap_ids_; // Maps from TrapKeys to numeric ids - ErrorCode *trap_array_; // Array of ErrorCodes indexed by ids - size_t trap_array_size_; // Currently used size of array - size_t trap_array_capacity_; // Currently allocated capacity of array - bool has_unsafe_traps_; // Whether unsafe traps have been enabled + TrapIds trap_ids_; // Maps from TrapKeys to numeric ids + ErrorCode* trap_array_; // Array of ErrorCodes indexed by ids + size_t trap_array_size_; // Currently used size of array + size_t trap_array_capacity_; // Currently allocated capacity of array + bool has_unsafe_traps_; // Whether unsafe traps have been enabled // Our constructor is private. A shared global instance is created // automatically as needed. @@ -118,6 +111,6 @@ class Trap { DISALLOW_IMPLICIT_CONSTRUCTORS(Trap); }; -} // namespace playground2 +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__ diff --git a/chromium/sandbox/linux/seccomp-bpf/verifier.cc b/chromium/sandbox/linux/seccomp-bpf/verifier.cc index 60c0eab20d6..1292504decc 100644 --- a/chromium/sandbox/linux/seccomp-bpf/verifier.cc +++ b/chromium/sandbox/linux/seccomp-bpf/verifier.cc @@ -5,37 +5,31 @@ #include #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/seccomp-bpf/verifier.h" -namespace { +namespace sandbox { -using playground2::ErrorCode; -using playground2::Sandbox; -using playground2::Verifier; -using playground2::arch_seccomp_data; +namespace { struct State { State(const std::vector& p, - const struct arch_seccomp_data& d) : - program(p), - data(d), - ip(0), - accumulator(0), - acc_is_valid(false) { - } + const struct arch_seccomp_data& d) + : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {} const std::vector& program; - const struct arch_seccomp_data& data; - unsigned int ip; - uint32_t accumulator; - bool acc_is_valid; + const struct arch_seccomp_data& data; + unsigned int ip; + uint32_t accumulator; + bool acc_is_valid; private: DISALLOW_IMPLICIT_CONSTRUCTORS(State); }; -uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code, +uint32_t EvaluateErrorCode(SandboxBPF* sandbox, + const ErrorCode& code, const struct arch_seccomp_data& data) { if (code.error_type() == ErrorCode::ET_SIMPLE || code.error_type() == ErrorCode::ET_TRAP) { @@ -44,49 +38,50 @@ uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code, if (code.width() == ErrorCode::TP_32BIT && (data.args[code.argno()] >> 32) && (data.args[code.argno()] & 0xFFFFFFFF80000000ull) != - 0xFFFFFFFF80000000ull) { + 0xFFFFFFFF80000000ull) { return sandbox->Unexpected64bitArgument().err(); } switch (code.op()) { - case ErrorCode::OP_EQUAL: - return EvaluateErrorCode(sandbox, - (code.width() == ErrorCode::TP_32BIT - ? uint32_t(data.args[code.argno()]) - : data.args[code.argno()]) == code.value() - ? *code.passed() - : *code.failed(), - data); - case ErrorCode::OP_HAS_ALL_BITS: - return EvaluateErrorCode(sandbox, - ((code.width() == ErrorCode::TP_32BIT - ? uint32_t(data.args[code.argno()]) - : data.args[code.argno()]) & code.value()) - == code.value() - ? *code.passed() - : *code.failed(), - data); - case ErrorCode::OP_HAS_ANY_BITS: - return EvaluateErrorCode(sandbox, - (code.width() == ErrorCode::TP_32BIT - ? uint32_t(data.args[code.argno()]) - : data.args[code.argno()]) & code.value() - ? *code.passed() - : *code.failed(), - data); - default: - return SECCOMP_RET_INVALID; + case ErrorCode::OP_EQUAL: + return EvaluateErrorCode(sandbox, + (code.width() == ErrorCode::TP_32BIT + ? uint32_t(data.args[code.argno()]) + : data.args[code.argno()]) == code.value() + ? *code.passed() + : *code.failed(), + data); + case ErrorCode::OP_HAS_ALL_BITS: + return EvaluateErrorCode(sandbox, + ((code.width() == ErrorCode::TP_32BIT + ? uint32_t(data.args[code.argno()]) + : data.args[code.argno()]) & + code.value()) == code.value() + ? *code.passed() + : *code.failed(), + data); + case ErrorCode::OP_HAS_ANY_BITS: + return EvaluateErrorCode(sandbox, + (code.width() == ErrorCode::TP_32BIT + ? uint32_t(data.args[code.argno()]) + : data.args[code.argno()]) & + code.value() + ? *code.passed() + : *code.failed(), + data); + default: + return SECCOMP_RET_INVALID; } } else { return SECCOMP_RET_INVALID; } } -bool VerifyErrorCode(Sandbox *sandbox, +bool VerifyErrorCode(SandboxBPF* sandbox, const std::vector& program, - struct arch_seccomp_data *data, + struct arch_seccomp_data* data, const ErrorCode& root_code, const ErrorCode& code, - const char **err) { + const char** err) { if (code.error_type() == ErrorCode::ET_SIMPLE || code.error_type() == ErrorCode::ET_TRAP) { uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err); @@ -109,102 +104,113 @@ bool VerifyErrorCode(Sandbox *sandbox, return false; } switch (code.op()) { - case ErrorCode::OP_EQUAL: - // Verify that we can check a 32bit value (or the LSB of a 64bit value) - // for equality. - data->args[code.argno()] = code.value(); - if (!VerifyErrorCode(sandbox, program, data, root_code, - *code.passed(), err)) { - return false; - } - - // Change the value to no longer match and verify that this is detected - // as an inequality. - data->args[code.argno()] = code.value() ^ 0x55AA55AA; - if (!VerifyErrorCode(sandbox, program, data, root_code, - *code.failed(), err)) { - return false; - } - - // BPF programs can only ever operate on 32bit values. So, we have - // generated additional BPF instructions that inspect the MSB. Verify - // that they behave as intended. - if (code.width() == ErrorCode::TP_32BIT) { - if (code.value() >> 32) { - SANDBOX_DIE("Invalid comparison of a 32bit system call argument " - "against a 64bit constant; this test is always false."); - } - - // If the system call argument was intended to be a 32bit parameter, - // verify that it is a fatal error if a 64bit value is ever passed - // here. - data->args[code.argno()] = 0x100000000ull; - if (!VerifyErrorCode(sandbox, program, data, root_code, - sandbox->Unexpected64bitArgument(), - err)) { + case ErrorCode::OP_EQUAL: + // Verify that we can check a 32bit value (or the LSB of a 64bit value) + // for equality. + data->args[code.argno()] = code.value(); + if (!VerifyErrorCode( + sandbox, program, data, root_code, *code.passed(), err)) { return false; } - } else { - // If the system call argument was intended to be a 64bit parameter, - // verify that we can handle (in-)equality for the MSB. This is - // essentially the same test that we did earlier for the LSB. - // We only need to verify the behavior of the inequality test. We - // know that the equality test already passed, as unlike the kernel - // the Verifier does operate on 64bit quantities. - data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull; - if (!VerifyErrorCode(sandbox, program, data, root_code, - *code.failed(), err)) { + + // Change the value to no longer match and verify that this is detected + // as an inequality. + data->args[code.argno()] = code.value() ^ 0x55AA55AA; + if (!VerifyErrorCode( + sandbox, program, data, root_code, *code.failed(), err)) { return false; } - } - break; - case ErrorCode::OP_HAS_ALL_BITS: - case ErrorCode::OP_HAS_ANY_BITS: - // A comprehensive test of bit values is difficult and potentially rather - // time-expensive. We avoid doing so at run-time and instead rely on the - // unittest for full testing. The test that we have here covers just the - // common cases. We test against the bitmask itself, all zeros and all - // ones. - { - // Testing "any" bits against a zero mask is always false. So, there - // are some cases, where we expect tests to take the "failed()" branch - // even though this is a test that normally should take "passed()". - const ErrorCode& passed = - (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) || - // On a 32bit system, it is impossible to pass a 64bit value as a - // system call argument. So, some additional tests always evaluate - // as false. - ((code.value() & ~uint64_t(uintptr_t(-1))) && - code.op() == ErrorCode::OP_HAS_ALL_BITS) || - (code.value() && !(code.value() & uintptr_t(-1)) && - code.op() == ErrorCode::OP_HAS_ANY_BITS) + // BPF programs can only ever operate on 32bit values. So, we have + // generated additional BPF instructions that inspect the MSB. Verify + // that they behave as intended. + if (code.width() == ErrorCode::TP_32BIT) { + if (code.value() >> 32) { + SANDBOX_DIE( + "Invalid comparison of a 32bit system call argument " + "against a 64bit constant; this test is always false."); + } - ? *code.failed() : *code.passed(); + // If the system call argument was intended to be a 32bit parameter, + // verify that it is a fatal error if a 64bit value is ever passed + // here. + data->args[code.argno()] = 0x100000000ull; + if (!VerifyErrorCode(sandbox, + program, + data, + root_code, + sandbox->Unexpected64bitArgument(), + err)) { + return false; + } + } else { + // If the system call argument was intended to be a 64bit parameter, + // verify that we can handle (in-)equality for the MSB. This is + // essentially the same test that we did earlier for the LSB. + // We only need to verify the behavior of the inequality test. We + // know that the equality test already passed, as unlike the kernel + // the Verifier does operate on 64bit quantities. + data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull; + if (!VerifyErrorCode( + sandbox, program, data, root_code, *code.failed(), err)) { + return false; + } + } + break; + case ErrorCode::OP_HAS_ALL_BITS: + case ErrorCode::OP_HAS_ANY_BITS: + // A comprehensive test of bit values is difficult and potentially + // rather + // time-expensive. We avoid doing so at run-time and instead rely on the + // unittest for full testing. The test that we have here covers just the + // common cases. We test against the bitmask itself, all zeros and all + // ones. + { + // Testing "any" bits against a zero mask is always false. So, there + // are some cases, where we expect tests to take the "failed()" branch + // even though this is a test that normally should take "passed()". + const ErrorCode& passed = + (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) || - // Similary, testing for "all" bits in a zero mask is always true. So, - // some cases pass despite them normally failing. - const ErrorCode& failed = - !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS - ? *code.passed() : *code.failed(); + // On a 32bit system, it is impossible to pass a 64bit + // value as a + // system call argument. So, some additional tests always + // evaluate + // as false. + ((code.value() & ~uint64_t(uintptr_t(-1))) && + code.op() == ErrorCode::OP_HAS_ALL_BITS) || + (code.value() && !(code.value() & uintptr_t(-1)) && + code.op() == ErrorCode::OP_HAS_ANY_BITS) + ? *code.failed() + : *code.passed(); - data->args[code.argno()] = code.value() & uintptr_t(-1); - if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) { - return false; - } - data->args[code.argno()] = uintptr_t(-1); - if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) { - return false; - } - data->args[code.argno()] = 0; - if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) { - return false; + // Similary, testing for "all" bits in a zero mask is always true. So, + // some cases pass despite them normally failing. + const ErrorCode& failed = + !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS + ? *code.passed() + : *code.failed(); + + data->args[code.argno()] = code.value() & uintptr_t(-1); + if (!VerifyErrorCode( + sandbox, program, data, root_code, passed, err)) { + return false; + } + data->args[code.argno()] = uintptr_t(-1); + if (!VerifyErrorCode( + sandbox, program, data, root_code, passed, err)) { + return false; + } + data->args[code.argno()] = 0; + if (!VerifyErrorCode( + sandbox, program, data, root_code, failed, err)) { + return false; + } } - } - break; - default: // TODO(markus): Need to add support for OP_GREATER - *err = "Unsupported operation in conditional error code"; - return false; + break; + default: // TODO(markus): Need to add support for OP_GREATER + *err = "Unsupported operation in conditional error code"; + return false; } } else { *err = "Attempting to return invalid error code from BPF program"; @@ -213,16 +219,15 @@ bool VerifyErrorCode(Sandbox *sandbox, return true; } -void Ld(State *state, const struct sock_filter& insn, const char **err) { - if (BPF_SIZE(insn.code) != BPF_W || - BPF_MODE(insn.code) != BPF_ABS) { +void Ld(State* state, const struct sock_filter& insn, const char** err) { + if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS) { *err = "Invalid BPF_LD instruction"; return; } if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) { // We only allow loading of properly aligned 32bit quantities. memcpy(&state->accumulator, - reinterpret_cast(&state->data) + insn.k, + reinterpret_cast(&state->data) + insn.k, 4); } else { *err = "Invalid operand in BPF_LD instruction"; @@ -232,7 +237,7 @@ void Ld(State *state, const struct sock_filter& insn, const char **err) { return; } -void Jmp(State *state, const struct sock_filter& insn, const char **err) { +void Jmp(State* state, const struct sock_filter& insn, const char** err) { if (BPF_OP(insn.code) == BPF_JA) { if (state->ip + insn.k + 1 >= state->program.size() || state->ip + insn.k + 1 <= state->ip) { @@ -242,48 +247,47 @@ void Jmp(State *state, const struct sock_filter& insn, const char **err) { } state->ip += insn.k; } else { - if (BPF_SRC(insn.code) != BPF_K || - !state->acc_is_valid || + if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid || state->ip + insn.jt + 1 >= state->program.size() || state->ip + insn.jf + 1 >= state->program.size()) { goto compilation_failure; } switch (BPF_OP(insn.code)) { - case BPF_JEQ: - if (state->accumulator == insn.k) { - state->ip += insn.jt; - } else { - state->ip += insn.jf; - } - break; - case BPF_JGT: - if (state->accumulator > insn.k) { - state->ip += insn.jt; - } else { - state->ip += insn.jf; - } - break; - case BPF_JGE: - if (state->accumulator >= insn.k) { - state->ip += insn.jt; - } else { - state->ip += insn.jf; - } - break; - case BPF_JSET: - if (state->accumulator & insn.k) { - state->ip += insn.jt; - } else { - state->ip += insn.jf; - } - break; - default: - goto compilation_failure; + case BPF_JEQ: + if (state->accumulator == insn.k) { + state->ip += insn.jt; + } else { + state->ip += insn.jf; + } + break; + case BPF_JGT: + if (state->accumulator > insn.k) { + state->ip += insn.jt; + } else { + state->ip += insn.jf; + } + break; + case BPF_JGE: + if (state->accumulator >= insn.k) { + state->ip += insn.jt; + } else { + state->ip += insn.jf; + } + break; + case BPF_JSET: + if (state->accumulator & insn.k) { + state->ip += insn.jt; + } else { + state->ip += insn.jf; + } + break; + default: + goto compilation_failure; } } } -uint32_t Ret(State *, const struct sock_filter& insn, const char **err) { +uint32_t Ret(State*, const struct sock_filter& insn, const char** err) { if (BPF_SRC(insn.code) != BPF_K) { *err = "Invalid BPF_RET instruction"; return 0; @@ -291,7 +295,7 @@ uint32_t Ret(State *, const struct sock_filter& insn, const char **err) { return insn.k; } -void Alu(State *state, const struct sock_filter& insn, const char **err) { +void Alu(State* state, const struct sock_filter& insn, const char** err) { if (BPF_OP(insn.code) == BPF_NEG) { state->accumulator = -state->accumulator; return; @@ -301,75 +305,67 @@ void Alu(State *state, const struct sock_filter& insn, const char **err) { return; } switch (BPF_OP(insn.code)) { - case BPF_ADD: - state->accumulator += insn.k; - break; - case BPF_SUB: - state->accumulator -= insn.k; - break; - case BPF_MUL: - state->accumulator *= insn.k; - break; - case BPF_DIV: - if (!insn.k) { - *err = "Illegal division by zero"; + case BPF_ADD: + state->accumulator += insn.k; break; - } - state->accumulator /= insn.k; - break; - case BPF_MOD: - if (!insn.k) { - *err = "Illegal division by zero"; + case BPF_SUB: + state->accumulator -= insn.k; break; - } - state->accumulator %= insn.k; - break; - case BPF_OR: - state->accumulator |= insn.k; - break; - case BPF_XOR: - state->accumulator ^= insn.k; - break; - case BPF_AND: - state->accumulator &= insn.k; - break; - case BPF_LSH: - if (insn.k > 32) { - *err = "Illegal shift operation"; + case BPF_MUL: + state->accumulator *= insn.k; break; - } - state->accumulator <<= insn.k; - break; - case BPF_RSH: - if (insn.k > 32) { - *err = "Illegal shift operation"; + case BPF_DIV: + if (!insn.k) { + *err = "Illegal division by zero"; + break; + } + state->accumulator /= insn.k; + break; + case BPF_MOD: + if (!insn.k) { + *err = "Illegal division by zero"; + break; + } + state->accumulator %= insn.k; + break; + case BPF_OR: + state->accumulator |= insn.k; + break; + case BPF_XOR: + state->accumulator ^= insn.k; + break; + case BPF_AND: + state->accumulator &= insn.k; + break; + case BPF_LSH: + if (insn.k > 32) { + *err = "Illegal shift operation"; + break; + } + state->accumulator <<= insn.k; + break; + case BPF_RSH: + if (insn.k > 32) { + *err = "Illegal shift operation"; + break; + } + state->accumulator >>= insn.k; + break; + default: + *err = "Invalid operator in arithmetic operation"; break; - } - state->accumulator >>= insn.k; - break; - default: - *err = "Invalid operator in arithmetic operation"; - break; } } } } // namespace -namespace playground2 { - -bool Verifier::VerifyBPF(Sandbox *sandbox, +bool Verifier::VerifyBPF(SandboxBPF* sandbox, const std::vector& program, - const Sandbox::Evaluators& evaluators, - const char **err) { + const SandboxBPFPolicy& policy, + const char** err) { *err = NULL; - if (evaluators.size() != 1) { - *err = "Not implemented"; - return false; - } - Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first; - void *aux = evaluators.begin()->second; - for (SyscallIterator iter(false); !iter.Done(); ) { + for (SyscallIterator iter(false); !iter.Done();) { uint32_t sysnum = iter.Next(); // We ideally want to iterate over the full system call range and values // just above and just below this range. This gives us the full result set @@ -378,8 +374,8 @@ bool Verifier::VerifyBPF(Sandbox *sandbox, // indicates either i386 or x86-64; and a set bit 30 indicates x32. And // unless we pay attention to setting this bit correctly, an early check in // our BPF program will make us fail with a misleading error code. - struct arch_seccomp_data data = { static_cast(sysnum), - static_cast(SECCOMP_ARCH) }; + struct arch_seccomp_data data = {static_cast(sysnum), + static_cast(SECCOMP_ARCH)}; #if defined(__i386__) || defined(__x86_64__) #if defined(__x86_64__) && defined(__ILP32__) if (!(sysnum & 0x40000000u)) { @@ -391,7 +387,7 @@ bool Verifier::VerifyBPF(Sandbox *sandbox, } #endif #endif - ErrorCode code = evaluate_syscall(sandbox, sysnum, aux); + ErrorCode code = policy.EvaluateSyscall(sandbox, sysnum); if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) { return false; } @@ -401,7 +397,7 @@ bool Verifier::VerifyBPF(Sandbox *sandbox, uint32_t Verifier::EvaluateBPF(const std::vector& program, const struct arch_seccomp_data& data, - const char **err) { + const char** err) { *err = NULL; if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) { *err = "Invalid program length"; @@ -414,36 +410,37 @@ uint32_t Verifier::EvaluateBPF(const std::vector& program, } const struct sock_filter& insn = program[state.ip]; switch (BPF_CLASS(insn.code)) { - case BPF_LD: - Ld(&state, insn, err); - break; - case BPF_JMP: - Jmp(&state, insn, err); - break; - case BPF_RET: { - uint32_t r = Ret(&state, insn, err); - switch (r & SECCOMP_RET_ACTION) { - case SECCOMP_RET_TRAP: - case SECCOMP_RET_ERRNO: - case SECCOMP_RET_ALLOW: + case BPF_LD: + Ld(&state, insn, err); break; - case SECCOMP_RET_KILL: // We don't ever generate this - case SECCOMP_RET_TRACE: // We don't ever generate this - case SECCOMP_RET_INVALID: // Should never show up in BPF program - default: - *err = "Unexpected return code found in BPF program"; - return 0; + case BPF_JMP: + Jmp(&state, insn, err); + break; + case BPF_RET: { + uint32_t r = Ret(&state, insn, err); + switch (r & SECCOMP_RET_ACTION) { + case SECCOMP_RET_TRAP: + case SECCOMP_RET_ERRNO: + case SECCOMP_RET_ALLOW: + break; + case SECCOMP_RET_KILL: // We don't ever generate this + case SECCOMP_RET_TRACE: // We don't ever generate this + case SECCOMP_RET_INVALID: // Should never show up in BPF program + default: + *err = "Unexpected return code found in BPF program"; + return 0; + } + return r; } - return r; } - case BPF_ALU: - Alu(&state, insn, err); - break; - default: - *err = "Unexpected instruction in BPF program"; - break; + case BPF_ALU: + Alu(&state, insn, err); + break; + default: + *err = "Unexpected instruction in BPF program"; + break; } } return 0; } -} // namespace +} // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/verifier.h b/chromium/sandbox/linux/seccomp-bpf/verifier.h index 3e99a0818b9..4e80dd97681 100644 --- a/chromium/sandbox/linux/seccomp-bpf/verifier.h +++ b/chromium/sandbox/linux/seccomp-bpf/verifier.h @@ -10,8 +10,9 @@ #include #include +namespace sandbox { -namespace playground2 { +class SandboxBPFPolicy; class Verifier { public: @@ -22,10 +23,10 @@ class Verifier { // set by the "evaluators". // Upon success, "err" is set to NULL. Upon failure, it contains a static // error message that does not need to be free()'d. - static bool VerifyBPF(Sandbox *sandbox, + static bool VerifyBPF(SandboxBPF* sandbox, const std::vector& program, - const Sandbox::Evaluators& evaluators, - const char **err); + const SandboxBPFPolicy& policy, + const char** err); // Evaluate a given BPF program for a particular set of system call // parameters. If evaluation failed for any reason, "err" will be set to @@ -37,12 +38,12 @@ class Verifier { // BPF compiler, we might have to extend this BPF interpreter. static uint32_t EvaluateBPF(const std::vector& program, const struct arch_seccomp_data& data, - const char **err); + const char** err); private: DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier); }; -} // namespace +} // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__ diff --git a/chromium/sandbox/linux/services/broker_process.cc b/chromium/sandbox/linux/services/broker_process.cc index 29222074e66..438e9726374 100644 --- a/chromium/sandbox/linux/services/broker_process.cc +++ b/chromium/sandbox/linux/services/broker_process.cc @@ -16,10 +16,12 @@ #include #include "base/basictypes.h" +#include "base/compiler_specific.h" #include "base/logging.h" #include "base/pickle.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/unix_domain_socket_linux.h" +#include "base/process/process_metrics.h" #include "build/build_config.h" #include "sandbox/linux/services/linux_syscalls.h" @@ -112,11 +114,13 @@ bool IsAllowedOpenFlags(int flags) { namespace sandbox { -BrokerProcess::BrokerProcess(const std::vector& allowed_r_files, +BrokerProcess::BrokerProcess(int denied_errno, + const std::vector& allowed_r_files, const std::vector& allowed_w_files, bool fast_check_in_client, bool quiet_failures_for_tests) - : initialized_(false), + : denied_errno_(denied_errno), + initialized_(false), is_child_(false), fast_check_in_client_(fast_check_in_client), quiet_failures_for_tests_(quiet_failures_for_tests), @@ -128,7 +132,7 @@ BrokerProcess::BrokerProcess(const std::vector& allowed_r_files, BrokerProcess::~BrokerProcess() { if (initialized_ && ipc_socketpair_ != -1) { - void (HANDLE_EINTR(close(ipc_socketpair_))); + close(ipc_socketpair_); } } @@ -143,15 +147,16 @@ bool BrokerProcess::Init(bool (*sandbox_callback)(void)) { return false; } + DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle())); int child_pid = fork(); if (child_pid == -1) { - (void) HANDLE_EINTR(close(socket_pair[0])); - (void) HANDLE_EINTR(close(socket_pair[1])); + close(socket_pair[0]); + close(socket_pair[1]); return false; } if (child_pid) { // We are the parent and we have just forked our broker process. - (void) HANDLE_EINTR(close(socket_pair[0])); + close(socket_pair[0]); // We should only be able to write to the IPC channel. We'll always send // a new file descriptor to receive the reply on. shutdown(socket_pair[1], SHUT_RD); @@ -162,7 +167,7 @@ bool BrokerProcess::Init(bool (*sandbox_callback)(void)) { return true; } else { // We are the broker. - (void) HANDLE_EINTR(close(socket_pair[1])); + close(socket_pair[1]); // We should only be able to read from this IPC channel. We will send our // replies on a new file descriptor attached to the requests. shutdown(socket_pair[0], SHUT_WR); @@ -218,11 +223,11 @@ int BrokerProcess::PathAndFlagsSyscall(enum IPCCommands syscall_type, if (fast_check_in_client_) { if (syscall_type == kCommandOpen && !GetFileNameIfAllowedToOpen(pathname, flags, NULL)) { - return -EPERM; + return -denied_errno_; } if (syscall_type == kCommandAccess && !GetFileNameIfAllowedToAccess(pathname, flags, NULL)) { - return -EPERM; + return -denied_errno_; } } @@ -278,7 +283,7 @@ int BrokerProcess::PathAndFlagsSyscall(enum IPCCommands syscall_type, } else { RAW_LOG(ERROR, "Could not read pickle"); NOTREACHED(); - return -EPERM; + return -ENOMEM; } } @@ -326,7 +331,7 @@ bool BrokerProcess::HandleRequest() const { r = false; break; } - int ret = HANDLE_EINTR(close(temporary_ipc)); + int ret = IGNORE_EINTR(close(temporary_ipc)); DCHECK(!ret) << "Could not close temporary IPC channel"; return r; } @@ -371,7 +376,7 @@ bool BrokerProcess::HandleRemoteCommand(IPCCommands command_type, int reply_ipc, // Close anything we have opened in this process. for (std::vector::iterator it = opened_files.begin(); it < opened_files.end(); ++it) { - int ret = HANDLE_EINTR(close(*it)); + int ret = IGNORE_EINTR(close(*it)); DCHECK(!ret) << "Could not close file descriptor"; } @@ -400,7 +405,7 @@ void BrokerProcess::AccessFileForIPC(const std::string& requested_filename, else write_pickle->WriteInt(-access_errno); } else { - write_pickle->WriteInt(-EPERM); + write_pickle->WriteInt(-denied_errno_); } } @@ -429,7 +434,7 @@ void BrokerProcess::OpenFileForIPC(const std::string& requested_filename, write_pickle->WriteInt(0); } } else { - write_pickle->WriteInt(-EPERM); + write_pickle->WriteInt(-denied_errno_); } } diff --git a/chromium/sandbox/linux/services/broker_process.h b/chromium/sandbox/linux/services/broker_process.h index 901ae5077c0..6b13b33046d 100644 --- a/chromium/sandbox/linux/services/broker_process.h +++ b/chromium/sandbox/linux/services/broker_process.h @@ -26,12 +26,17 @@ namespace sandbox { // 4. Use open_broker.Open() to open files. class BrokerProcess { public: - // |allowed_file_names| is a white list of files that can be opened later via - // the Open() API. + // |denied_errno| is the error code returned when methods such as Open() + // or Access() are invoked on a file which is not in the whitelist. EACCESS + // would be a typical value. + // |allowed_r_files| and |allowed_w_files| are white lists of files that can + // be opened later via the Open() API, respectively for reading and writing. + // A file available read-write should be listed in both. // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for // unit tests, don't use it. - explicit BrokerProcess(const std::vector& allowed_r_files_, - const std::vector& allowed_w_files_, + explicit BrokerProcess(int denied_errno, + const std::vector& allowed_r_files, + const std::vector& allowed_w_files, bool fast_check_in_client = true, bool quiet_failures_for_tests = false); ~BrokerProcess(); @@ -42,8 +47,8 @@ class BrokerProcess { bool Init(bool (*sandbox_callback)(void)); // Can be used in place of access(). Will be async signal safe. - // X_OK will always EPERM in practice since the broker process doesn't support - // execute permissions. + // X_OK will always return an error in practice since the broker process + // doesn't support execute permissions. // It's similar to the access() system call and will return -errno on errors. int Access(const char* pathname, int mode) const; // Can be used in place of open(). Will be async signal safe. @@ -61,25 +66,31 @@ class BrokerProcess { kCommandAccess, }; int PathAndFlagsSyscall(enum IPCCommands command_type, - const char* pathname, int flags) const; + const char* pathname, + int flags) const; bool HandleRequest() const; - bool HandleRemoteCommand(IPCCommands command_type, int reply_ipc, - const Pickle& read_pickle, PickleIterator iter) const; + bool HandleRemoteCommand(IPCCommands command_type, + int reply_ipc, + const Pickle& read_pickle, + PickleIterator iter) const; void AccessFileForIPC(const std::string& requested_filename, - int mode, Pickle* write_pickle) const; + int mode, + Pickle* write_pickle) const; void OpenFileForIPC(const std::string& requested_filename, - int flags, Pickle* write_pickle, + int flags, + Pickle* write_pickle, std::vector* opened_files) const; bool GetFileNameIfAllowedToAccess(const char*, int, const char**) const; bool GetFileNameIfAllowedToOpen(const char*, int, const char**) const; - bool initialized_; // Whether we've been through Init() yet. - bool is_child_; // Whether we're the child (broker process). - bool fast_check_in_client_; // Whether to forward a request that we know - // will be denied to the broker. + const int denied_errno_; + bool initialized_; // Whether we've been through Init() yet. + bool is_child_; // Whether we're the child (broker process). + bool fast_check_in_client_; // Whether to forward a request that we know + // will be denied to the broker. bool quiet_failures_for_tests_; // Disable certain error message when // testing for failures. - pid_t broker_pid_; // The PID of the broker (child). + pid_t broker_pid_; // The PID of the broker (child). const std::vector allowed_r_files_; // Files allowed for read. const std::vector allowed_w_files_; // Files allowed for write. int ipc_socketpair_; // Our communication channel to parent or child. diff --git a/chromium/sandbox/linux/services/broker_process_unittest.cc b/chromium/sandbox/linux/services/broker_process_unittest.cc index 8ff4e00c8ec..f163ef99718 100644 --- a/chromium/sandbox/linux/services/broker_process_unittest.cc +++ b/chromium/sandbox/linux/services/broker_process_unittest.cc @@ -15,11 +15,15 @@ #include #include "base/basictypes.h" +#include "base/file_util.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" #include "sandbox/linux/tests/unit_tests.h" #include "testing/gtest/include/gtest/gtest.h" +using file_util::ScopedFD; + namespace sandbox { namespace { @@ -44,7 +48,7 @@ class ScopedTemporaryFile { } ~ScopedTemporaryFile() { CHECK_EQ(0, unlink(full_file_name_)); - CHECK_EQ(0, HANDLE_EINTR(close(fd_))); + CHECK_EQ(0, IGNORE_EINTR(close(fd_))); } int fd() const { return fd_; } @@ -68,13 +72,13 @@ TEST(BrokerProcess, CreateAndDestroy) { std::vector read_whitelist; read_whitelist.push_back("/proc/cpuinfo"); - BrokerProcess* open_broker = new BrokerProcess(read_whitelist, - std::vector()); + scoped_ptr open_broker( + new BrokerProcess(EPERM, read_whitelist, std::vector())); ASSERT_TRUE(open_broker->Init(NULL)); pid_t broker_pid = open_broker->broker_pid(); - delete(open_broker); - // Now we check that the broker has exited properly. + // Destroy the broker and check it has exited properly. + open_broker.reset(); int status = 0; ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid); ASSERT_TRUE(WIFEXITED(status)); @@ -83,7 +87,7 @@ TEST(BrokerProcess, CreateAndDestroy) { TEST(BrokerProcess, TestOpenAccessNull) { const std::vector empty; - BrokerProcess open_broker(empty, empty); + BrokerProcess open_broker(EPERM, empty, empty); ASSERT_TRUE(open_broker.Init(NULL)); int fd = open_broker.Open(NULL, O_RDONLY); @@ -93,7 +97,7 @@ TEST(BrokerProcess, TestOpenAccessNull) { ASSERT_EQ(ret, -EFAULT); } -void TestOpenFilePerms(bool fast_check_in_client) { +void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) { const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1"; // We can't debug the init process, and shouldn't be able to access // its auxv file. @@ -111,7 +115,8 @@ void TestOpenFilePerms(bool fast_check_in_client) { write_whitelist.push_back(kW_WhiteListed); write_whitelist.push_back(kRW_WhiteListed); - BrokerProcess open_broker(read_whitelist, + BrokerProcess open_broker(denied_errno, + read_whitelist, write_whitelist, fast_check_in_client); ASSERT_TRUE(open_broker.Init(NULL)); @@ -120,22 +125,22 @@ void TestOpenFilePerms(bool fast_check_in_client) { fd = open_broker.Open(kR_WhiteListed, O_RDONLY); ASSERT_EQ(fd, -ENOENT); fd = open_broker.Open(kR_WhiteListed, O_WRONLY); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); fd = open_broker.Open(kR_WhiteListed, O_RDWR); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); int ret = -1; ret = open_broker.Access(kR_WhiteListed, F_OK); ASSERT_EQ(ret, -ENOENT); ret = open_broker.Access(kR_WhiteListed, R_OK); ASSERT_EQ(ret, -ENOENT); ret = open_broker.Access(kR_WhiteListed, W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kR_WhiteListed, R_OK | W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kR_WhiteListed, X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kR_WhiteListed, R_OK | X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); // Android sometimes runs tests as root. // This part of the test requires a process that doesn't have @@ -146,42 +151,42 @@ void TestOpenFilePerms(bool fast_check_in_client) { // won't. ASSERT_EQ(fd, -EACCES); fd = open_broker.Open(kR_WhiteListedButDenied, O_WRONLY); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); fd = open_broker.Open(kR_WhiteListedButDenied, O_RDWR); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); ret = open_broker.Access(kR_WhiteListedButDenied, F_OK); // The normal permission system will let us check that the file exists. ASSERT_EQ(ret, 0); ret = open_broker.Access(kR_WhiteListedButDenied, R_OK); ASSERT_EQ(ret, -EACCES); ret = open_broker.Access(kR_WhiteListedButDenied, W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kR_WhiteListedButDenied, X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); } fd = open_broker.Open(kW_WhiteListed, O_RDONLY); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); fd = open_broker.Open(kW_WhiteListed, O_WRONLY); ASSERT_EQ(fd, -ENOENT); fd = open_broker.Open(kW_WhiteListed, O_RDWR); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); ret = open_broker.Access(kW_WhiteListed, F_OK); ASSERT_EQ(ret, -ENOENT); ret = open_broker.Access(kW_WhiteListed, R_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kW_WhiteListed, W_OK); ASSERT_EQ(ret, -ENOENT); ret = open_broker.Access(kW_WhiteListed, R_OK | W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kW_WhiteListed, X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kW_WhiteListed, R_OK | X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); fd = open_broker.Open(kRW_WhiteListed, O_RDONLY); ASSERT_EQ(fd, -ENOENT); @@ -198,68 +203,79 @@ void TestOpenFilePerms(bool fast_check_in_client) { ret = open_broker.Access(kRW_WhiteListed, R_OK | W_OK); ASSERT_EQ(ret, -ENOENT); ret = open_broker.Access(kRW_WhiteListed, X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(kRW_WhiteListed, R_OK | X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); fd = open_broker.Open(k_NotWhitelisted, O_RDONLY); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); fd = open_broker.Open(k_NotWhitelisted, O_WRONLY); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); fd = open_broker.Open(k_NotWhitelisted, O_RDWR); - ASSERT_EQ(fd, -EPERM); + ASSERT_EQ(fd, -denied_errno); ret = open_broker.Access(k_NotWhitelisted, F_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(k_NotWhitelisted, R_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(k_NotWhitelisted, W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(k_NotWhitelisted, R_OK | W_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(k_NotWhitelisted, X_OK); - ASSERT_EQ(ret, -EPERM); + ASSERT_EQ(ret, -denied_errno); ret = open_broker.Access(k_NotWhitelisted, R_OK | X_OK); - ASSERT_EQ(ret, -EPERM); - + ASSERT_EQ(ret, -denied_errno); // We have some extra sanity check for clearly wrong values. - fd = open_broker.Open(kRW_WhiteListed, O_RDONLY|O_WRONLY|O_RDWR); - ASSERT_EQ(fd, -EPERM); + fd = open_broker.Open(kRW_WhiteListed, O_RDONLY | O_WRONLY | O_RDWR); + ASSERT_EQ(fd, -denied_errno); // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this // is denied. - fd = open_broker.Open(kRW_WhiteListed, O_RDWR|O_CREAT); - ASSERT_EQ(fd, -EPERM); + fd = open_broker.Open(kRW_WhiteListed, O_RDWR | O_CREAT); + ASSERT_EQ(fd, -denied_errno); } // Run the same thing twice. The second time, we make sure that no security // check is performed on the client. TEST(BrokerProcess, OpenFilePermsWithClientCheck) { - TestOpenFilePerms(true /* fast_check_in_client */); + TestOpenFilePerms(true /* fast_check_in_client */, EPERM); // Don't do anything here, so that ASSERT works in the subfunction as // expected. } TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) { - TestOpenFilePerms(false /* fast_check_in_client */); + TestOpenFilePerms(false /* fast_check_in_client */, EPERM); + // Don't do anything here, so that ASSERT works in the subfunction as + // expected. +} + +// Run the same twice again, but with ENOENT instead of EPERM. +TEST(BrokerProcess, OpenFilePermsWithClientCheckNoEnt) { + TestOpenFilePerms(true /* fast_check_in_client */, ENOENT); // Don't do anything here, so that ASSERT works in the subfunction as // expected. } +TEST(BrokerProcess, OpenOpenFilePermsNoClientCheckNoEnt) { + TestOpenFilePerms(false /* fast_check_in_client */, ENOENT); + // Don't do anything here, so that ASSERT works in the subfunction as + // expected. +} void TestOpenCpuinfo(bool fast_check_in_client) { const char kFileCpuInfo[] = "/proc/cpuinfo"; std::vector read_whitelist; read_whitelist.push_back(kFileCpuInfo); - BrokerProcess* open_broker = new BrokerProcess(read_whitelist, - std::vector(), - fast_check_in_client); + scoped_ptr open_broker(new BrokerProcess( + EPERM, read_whitelist, std::vector(), fast_check_in_client)); ASSERT_TRUE(open_broker->Init(NULL)); pid_t broker_pid = open_broker->broker_pid(); int fd = -1; fd = open_broker->Open(kFileCpuInfo, O_RDWR); + ScopedFD fd_closer(&fd); ASSERT_EQ(fd, -EPERM); // Check we can read /proc/cpuinfo. @@ -271,6 +287,7 @@ void TestOpenCpuinfo(bool fast_check_in_client) { // Open cpuinfo via the broker. int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY); + ScopedFD cpuinfo_fd_closer(&cpuinfo_fd); ASSERT_GE(cpuinfo_fd, 0); char buf[3]; memset(buf, 0, sizeof(buf)); @@ -279,6 +296,7 @@ void TestOpenCpuinfo(bool fast_check_in_client) { // Open cpuinfo directly. int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY); + ScopedFD cpuinfo_fd2_closer(&cpuinfo_fd2); ASSERT_GE(cpuinfo_fd2, 0); char buf2[3]; memset(buf2, 1, sizeof(buf2)); @@ -291,14 +309,7 @@ void TestOpenCpuinfo(bool fast_check_in_client) { // ourselves. ASSERT_EQ(memcmp(buf, buf2, read_len1), 0); - if (fd >= 0) - close(fd); - if (cpuinfo_fd >= 0) - close(cpuinfo_fd); - if (cpuinfo_fd2 >= 0) - close(cpuinfo_fd); - - delete(open_broker); + open_broker.reset(); // Now we check that the broker has exited properly. int status = 0; @@ -328,7 +339,7 @@ TEST(BrokerProcess, OpenFileRW) { std::vector whitelist; whitelist.push_back(tempfile_name); - BrokerProcess open_broker(whitelist, whitelist); + BrokerProcess open_broker(EPERM, whitelist, whitelist); ASSERT_TRUE(open_broker.Init(NULL)); // Check we can access that file with read or write. @@ -355,12 +366,14 @@ TEST(BrokerProcess, OpenFileRW) { ASSERT_EQ(close(tempfile2), 0); } -// Sandbox test because we could get a SIGPIPE. +// SANDBOX_TEST because the process could die with a SIGPIPE +// and we want this to happen in a subprocess. SANDBOX_TEST(BrokerProcess, BrokerDied) { std::vector read_whitelist; read_whitelist.push_back("/proc/cpuinfo"); - BrokerProcess open_broker(read_whitelist, + BrokerProcess open_broker(EPERM, + read_whitelist, std::vector(), true /* fast_check_in_client */, true /* quiet_failures_for_tests */); @@ -373,7 +386,7 @@ SANDBOX_TEST(BrokerProcess, BrokerDied) { SANDBOX_ASSERT(waitpid(broker_pid, &status, 0) == broker_pid); SANDBOX_ASSERT(WIFSIGNALED(status)); SANDBOX_ASSERT(WTERMSIG(status) == SIGKILL); - // Hopefully doing Open with a dead broker won't SIGPIPE us. + // Check that doing Open with a dead broker won't SIGPIPE us. SANDBOX_ASSERT(open_broker.Open("/proc/cpuinfo", O_RDONLY) == -ENOMEM); SANDBOX_ASSERT(open_broker.Access("/proc/cpuinfo", O_RDONLY) == -ENOMEM); } @@ -383,7 +396,8 @@ void TestOpenComplexFlags(bool fast_check_in_client) { std::vector whitelist; whitelist.push_back(kCpuInfo); - BrokerProcess open_broker(whitelist, + BrokerProcess open_broker(EPERM, + whitelist, whitelist, fast_check_in_client); ASSERT_TRUE(open_broker.Init(NULL)); diff --git a/chromium/sandbox/linux/services/credentials.cc b/chromium/sandbox/linux/services/credentials.cc new file mode 100644 index 00000000000..4f041dc2887 --- /dev/null +++ b/chromium/sandbox/linux/services/credentials.cc @@ -0,0 +1,272 @@ +// Copyright (c) 2013 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/linux/services/credentials.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/template_util.h" +#include "base/threading/thread.h" + +namespace { + +struct CapFreeDeleter { + inline void operator()(cap_t cap) const { + int ret = cap_free(cap); + CHECK_EQ(0, ret); + } +}; + +// Wrapper to manage libcap2's cap_t type. +typedef scoped_ptr ScopedCap; + +struct CapTextFreeDeleter { + inline void operator()(char* cap_text) const { + int ret = cap_free(cap_text); + CHECK_EQ(0, ret); + } +}; + +// Wrapper to manage the result from libcap2's cap_from_text(). +typedef scoped_ptr ScopedCapText; + +struct FILECloser { + inline void operator()(FILE* f) const { + DCHECK(f); + PCHECK(0 == fclose(f)); + } +}; + +// Don't use ScopedFILE in base/file_util.h since it doesn't check fclose(). +// TODO(jln): fix base/. +typedef scoped_ptr ScopedFILE; + +struct DIRCloser { + void operator()(DIR* d) const { + DCHECK(d); + PCHECK(0 == closedir(d)); + } +}; + +typedef scoped_ptr ScopedDIR; + +COMPILE_ASSERT((base::is_same::value), UidAndGidAreSameType); +// generic_id_t can be used for either uid_t or gid_t. +typedef uid_t generic_id_t; + +// Write a uid or gid mapping from |id| to |id| in |map_file|. +bool WriteToIdMapFile(const char* map_file, generic_id_t id) { + ScopedFILE f(fopen(map_file, "w")); + PCHECK(f); + const uid_t inside_id = id; + const uid_t outside_id = id; + int num = fprintf(f.get(), "%d %d 1\n", inside_id, outside_id); + if (num < 0) return false; + // Manually call fflush() to catch permission failures. + int ret = fflush(f.get()); + if (ret) { + VLOG(1) << "Could not write to id map file"; + return false; + } + return true; +} + +// Checks that the set of RES-uids and the set of RES-gids have +// one element each and return that element in |resuid| and |resgid| +// respectively. It's ok to pass NULL as one or both of the ids. +bool GetRESIds(uid_t* resuid, gid_t* resgid) { + uid_t ruid, euid, suid; + gid_t rgid, egid, sgid; + PCHECK(getresuid(&ruid, &euid, &suid) == 0); + PCHECK(getresgid(&rgid, &egid, &sgid) == 0); + const bool uids_are_equal = (ruid == euid) && (ruid == suid); + const bool gids_are_equal = (rgid == egid) && (rgid == sgid); + if (!uids_are_equal || !gids_are_equal) return false; + if (resuid) *resuid = euid; + if (resgid) *resgid = egid; + return true; +} + +// chroot() and chdir() to /proc//fdinfo. +void ChrootToThreadFdInfo(base::PlatformThreadId tid, bool* result) { + DCHECK(result); + *result = false; + + COMPILE_ASSERT((base::is_same::value), + TidIsAnInt); + const std::string current_thread_fdinfo = "/proc/" + + base::IntToString(tid) + "/fdinfo/"; + + // Make extra sure that /proc//fdinfo is unique to the thread. + CHECK(0 == unshare(CLONE_FILES)); + int chroot_ret = chroot(current_thread_fdinfo.c_str()); + if (chroot_ret) { + PLOG(ERROR) << "Could not chroot"; + return; + } + + // CWD is essentially an implicit file descriptor, so be careful to not leave + // it behind. + PCHECK(0 == chdir("/")); + + *result = true; + return; +} + +// chroot() to an empty dir that is "safe". To be safe, it must not contain +// any subdirectory (chroot-ing there would allow a chroot escape) and it must +// be impossible to create an empty directory there. +// We achieve this by doing the following: +// 1. We create a new thread, which will create a new /proc// directory +// 2. We chroot to /proc//fdinfo/ +// This is already "safe", since fdinfo/ does not contain another directory and +// one cannot create another directory there. +// 3. The thread dies +// After (3) happens, the directory is not available anymore in /proc. +bool ChrootToSafeEmptyDir() { + base::Thread chrooter("sandbox_chrooter"); + if (!chrooter.Start()) return false; + bool is_chrooted = false; + chrooter.message_loop()->PostTask(FROM_HERE, + base::Bind(&ChrootToThreadFdInfo, chrooter.thread_id(), &is_chrooted)); + // Make sure our task has run before committing the return value. + chrooter.Stop(); + return is_chrooted; +} + +} // namespace. + +namespace sandbox { + +Credentials::Credentials() { +} + +Credentials::~Credentials() { +} + +bool Credentials::HasOpenDirectory(int proc_fd) { + int proc_self_fd = -1; + if (proc_fd >= 0) { + proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY); + } else { + proc_self_fd = openat(AT_FDCWD, "/proc/self/fd", O_DIRECTORY | O_RDONLY); + if (proc_self_fd < 0) { + // If this process has been chrooted (eg into /proc/self/fdinfo) then + // the new root dir will not have directory listing permissions for us + // (hence EACCES). And if we do have this permission, then /proc won't + // exist anyway (hence ENOENT). + DPCHECK(errno == EACCES || errno == ENOENT) + << "Unexpected failure when trying to open /proc/self/fd: (" + << errno << ") " << strerror(errno); + + // If not available, guess false. + return false; + } + } + CHECK_GE(proc_self_fd, 0); + + // Ownership of proc_self_fd is transferred here, it must not be closed + // or modified afterwards except via dir. + ScopedDIR dir(fdopendir(proc_self_fd)); + CHECK(dir); + + struct dirent e; + struct dirent* de; + while (!readdir_r(dir.get(), &e, &de) && de) { + if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) { + continue; + } + + int fd_num; + CHECK(base::StringToInt(e.d_name, &fd_num)); + if (fd_num == proc_fd || fd_num == proc_self_fd) { + continue; + } + + struct stat s; + // It's OK to use proc_self_fd here, fstatat won't modify it. + CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0); + if (S_ISDIR(s.st_mode)) { + return true; + } + } + + // No open unmanaged directories found. + return false; +} + +bool Credentials::DropAllCapabilities() { + ScopedCap cap(cap_init()); + CHECK(cap); + PCHECK(0 == cap_set_proc(cap.get())); + // We never let this function fail. + return true; +} + +bool Credentials::HasAnyCapability() const { + ScopedCap current_cap(cap_get_proc()); + CHECK(current_cap); + ScopedCap empty_cap(cap_init()); + CHECK(empty_cap); + return cap_compare(current_cap.get(), empty_cap.get()) != 0; +} + +scoped_ptr Credentials::GetCurrentCapString() const { + ScopedCap current_cap(cap_get_proc()); + CHECK(current_cap); + ScopedCapText cap_text(cap_to_text(current_cap.get(), NULL)); + CHECK(cap_text); + return scoped_ptr (new std::string(cap_text.get())); +} + +bool Credentials::MoveToNewUserNS() { + uid_t uid; + gid_t gid; + if (!GetRESIds(&uid, &gid)) { + // If all the uids (or gids) are not equal to each other, the security + // model will most likely confuse the caller, abort. + DVLOG(1) << "uids or gids differ!"; + return false; + } + int ret = unshare(CLONE_NEWUSER); + // EPERM can happen if already in a chroot. EUSERS if too many nested + // namespaces are used. EINVAL for kernels that don't support the feature. + // Valgrind will ENOSYS unshare(). + PCHECK(!ret || errno == EPERM || errno == EUSERS || errno == EINVAL || + errno == ENOSYS); + if (ret) { + VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " + << "on this kernel."; + return false; + } + // The current {r,e,s}{u,g}id is now an overflow id (c.f. + // /proc/sys/kernel/overflowuid). Setup the uid and gid maps. + DCHECK(GetRESIds(NULL, NULL)); + const char kGidMapFile[] = "/proc/self/gid_map"; + const char kUidMapFile[] = "/proc/self/uid_map"; + CHECK(WriteToIdMapFile(kGidMapFile, gid)); + CHECK(WriteToIdMapFile(kUidMapFile, uid)); + DCHECK(GetRESIds(NULL, NULL)); + return true; +} + +bool Credentials::DropFileSystemAccess() { + // Chrooting to a safe empty dir will only be safe if no directory file + // descriptor is available to the process. + DCHECK(!HasOpenDirectory(-1)); + return ChrootToSafeEmptyDir(); +} + +} // namespace sandbox. diff --git a/chromium/sandbox/linux/services/credentials.h b/chromium/sandbox/linux/services/credentials.h new file mode 100644 index 00000000000..c23db930df2 --- /dev/null +++ b/chromium/sandbox/linux/services/credentials.h @@ -0,0 +1,79 @@ +// Copyright (c) 2013 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_LINUX_SERVICES_CREDENTIALS_H_ +#define SANDBOX_LINUX_SERVICES_CREDENTIALS_H_ + +#include "build/build_config.h" +// Link errors are tedious to track, raise a compile-time error instead. +#if defined(OS_ANDROID) +#error "Android is not supported." +#endif // defined(OS_ANDROID). + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace sandbox { + +// This class should be used to manipulate the current process' credentials. +// It is currently a stub used to manipulate POSIX.1e capabilities as +// implemented by the Linux kernel. +class Credentials { + public: + Credentials(); + ~Credentials(); + + // Checks whether the current process has any directory file descriptor open. + // Directory file descriptors are "capabilities" that would let a process use + // system calls such as openat() to bypass restrictions such as + // DropFileSystemAccess(). + // Sometimes it's useful to call HasOpenDirectory() after file system access + // has been dropped. In this case, |proc_fd| should be a file descriptor to + // /proc. The file descriptor in |proc_fd| will be ignored by + // HasOpenDirectory() and remains owned by the caller. It is very important + // for the caller to close it. + // If /proc is available, |proc_fd| can be passed as -1. + // If |proc_fd| is -1 and /proc is not available, this function will return + // false. + bool HasOpenDirectory(int proc_fd); + + // Drop all capabilities in the effective, inheritable and permitted sets for + // the current process. + bool DropAllCapabilities(); + // Return true iff there is any capability in any of the capabilities sets + // of the current process. + bool HasAnyCapability() const; + // Returns the capabilities of the current process in textual form, as + // documented in libcap2's cap_to_text(3). This is mostly useful for + // debugging and tests. + scoped_ptr GetCurrentCapString() const; + + // Move the current process to a new "user namespace" as supported by Linux + // 3.8+ (CLONE_NEWUSER). + // The uid map will be set-up so that the perceived uid and gid will not + // change. + // If this call succeeds, the current process will be granted a full set of + // capabilities in the new namespace. + bool MoveToNewUserNS(); + + // Remove the ability of the process to access the file system. File + // descriptors which are already open prior to calling this API remain + // available. + // The implementation currently uses chroot(2) and requires CAP_SYS_CHROOT. + // CAP_SYS_CHROOT can be acquired by using the MoveToNewUserNS() API. + // Make sure to call DropAllCapabilities() after this call to prevent + // escapes. + // To be secure, it's very important for this API to not be called while the + // process has any directory file descriptor open. + bool DropFileSystemAccess(); + + private: + DISALLOW_COPY_AND_ASSIGN(Credentials); +}; + +} // namespace sandbox. + +#endif // SANDBOX_LINUX_SERVICES_CREDENTIALS_H_ diff --git a/chromium/sandbox/linux/services/credentials_unittest.cc b/chromium/sandbox/linux/services/credentials_unittest.cc new file mode 100644 index 00000000000..9160bf7a1ca --- /dev/null +++ b/chromium/sandbox/linux/services/credentials_unittest.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2012 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/linux/services/credentials.h" + +#include +#include +#include +#include +#include +#include + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" + +using file_util::ScopedFD; + +namespace sandbox { + +namespace { + +bool DirectoryExists(const char* path) { + struct stat dir; + errno = 0; + int ret = stat(path, &dir); + return -1 != ret || ENOENT != errno; +} + +bool WorkingDirectoryIsRoot() { + char current_dir[PATH_MAX]; + char* cwd = getcwd(current_dir, sizeof(current_dir)); + PCHECK(cwd); + if (strcmp("/", cwd)) return false; + + // The current directory is the root. Add a few paranoid checks. + struct stat current; + CHECK_EQ(0, stat(".", ¤t)); + struct stat parrent; + CHECK_EQ(0, stat("..", &parrent)); + CHECK_EQ(current.st_dev, parrent.st_dev); + CHECK_EQ(current.st_ino, parrent.st_ino); + CHECK_EQ(current.st_mode, parrent.st_mode); + CHECK_EQ(current.st_uid, parrent.st_uid); + CHECK_EQ(current.st_gid, parrent.st_gid); + return true; +} + +// Give dynamic tools a simple thing to test. +TEST(Credentials, CreateAndDestroy) { + { + Credentials cred1; + (void) cred1; + } + scoped_ptr cred2(new Credentials); +} + +TEST(Credentials, HasOpenDirectory) { + Credentials creds; + // No open directory should exist at startup. + EXPECT_FALSE(creds.HasOpenDirectory(-1)); + { + // Have a "/dev" file descriptor around. + int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY); + ScopedFD dev_fd_closer(&dev_fd); + EXPECT_TRUE(creds.HasOpenDirectory(-1)); + } + EXPECT_FALSE(creds.HasOpenDirectory(-1)); +} + +TEST(Credentials, HasOpenDirectoryWithFD) { + Credentials creds; + + int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY); + ScopedFD proc_fd_closer(&proc_fd); + ASSERT_LE(0, proc_fd); + + // Don't pass |proc_fd|, an open directory (proc_fd) should + // be detected. + EXPECT_TRUE(creds.HasOpenDirectory(-1)); + // Pass |proc_fd| and no open directory should be detected. + EXPECT_FALSE(creds.HasOpenDirectory(proc_fd)); + + { + // Have a "/dev" file descriptor around. + int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY); + ScopedFD dev_fd_closer(&dev_fd); + EXPECT_TRUE(creds.HasOpenDirectory(proc_fd)); + } + + // The "/dev" file descriptor should now be closed, |proc_fd| is the only + // directory file descriptor open. + EXPECT_FALSE(creds.HasOpenDirectory(proc_fd)); +} + +SANDBOX_TEST(Credentials, DropAllCaps) { + Credentials creds; + CHECK(creds.DropAllCapabilities()); + CHECK(!creds.HasAnyCapability()); +} + +SANDBOX_TEST(Credentials, GetCurrentCapString) { + Credentials creds; + CHECK(creds.DropAllCapabilities()); + const char kNoCapabilityText[] = "="; + CHECK(*creds.GetCurrentCapString() == kNoCapabilityText); +} + +SANDBOX_TEST(Credentials, MoveToNewUserNS) { + Credentials creds; + creds.DropAllCapabilities(); + bool userns_supported = creds.MoveToNewUserNS(); + fprintf(stdout, "Unprivileged CLONE_NEWUSER supported: %s\n", + userns_supported ? "true." : "false."); + fflush(stdout); + if (!userns_supported) { + fprintf(stdout, "This kernel does not support unprivileged namespaces. " + "USERNS tests will succeed without running.\n"); + fflush(stdout); + return; + } + CHECK(creds.HasAnyCapability()); + creds.DropAllCapabilities(); + CHECK(!creds.HasAnyCapability()); +} + +SANDBOX_TEST(Credentials, UidIsPreserved) { + Credentials creds; + creds.DropAllCapabilities(); + uid_t old_ruid, old_euid, old_suid; + gid_t old_rgid, old_egid, old_sgid; + PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid)); + PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid)); + // Probably missing kernel support. + if (!creds.MoveToNewUserNS()) return; + uid_t new_ruid, new_euid, new_suid; + PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid)); + CHECK(old_ruid == new_ruid); + CHECK(old_euid == new_euid); + CHECK(old_suid == new_suid); + + gid_t new_rgid, new_egid, new_sgid; + PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid)); + CHECK(old_rgid == new_rgid); + CHECK(old_egid == new_egid); + CHECK(old_sgid == new_sgid); +} + +bool NewUserNSCycle(Credentials* creds) { + DCHECK(creds); + if (!creds->MoveToNewUserNS() || + !creds->HasAnyCapability() || + !creds->DropAllCapabilities() || + creds->HasAnyCapability()) { + return false; + } + return true; +} + +SANDBOX_TEST(Credentials, NestedUserNS) { + Credentials creds; + CHECK(creds.DropAllCapabilities()); + // Probably missing kernel support. + if (!creds.MoveToNewUserNS()) return; + creds.DropAllCapabilities(); + // As of 3.12, the kernel has a limit of 32. See create_user_ns(). + const int kNestLevel = 10; + for (int i = 0; i < kNestLevel; ++i) { + CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration " + << i << "."; + } +} + +// Test the WorkingDirectoryIsRoot() helper. +TEST(Credentials, CanDetectRoot) { + ASSERT_EQ(0, chdir("/proc/")); + ASSERT_FALSE(WorkingDirectoryIsRoot()); + ASSERT_EQ(0, chdir("/")); + ASSERT_TRUE(WorkingDirectoryIsRoot()); +} + +SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) { + Credentials creds; + CHECK(creds.DropAllCapabilities()); + // Probably missing kernel support. + if (!creds.MoveToNewUserNS()) return; + CHECK(creds.DropFileSystemAccess()); + CHECK(!DirectoryExists("/proc")); + CHECK(WorkingDirectoryIsRoot()); + // We want the chroot to never have a subdirectory. A subdirectory + // could allow a chroot escape. + CHECK_NE(0, mkdir("/test", 0700)); +} + +// Check that after dropping filesystem access and dropping privileges +// it is not possible to regain capabilities. +SANDBOX_TEST(Credentials, CannotRegainPrivileges) { + Credentials creds; + CHECK(creds.DropAllCapabilities()); + // Probably missing kernel support. + if (!creds.MoveToNewUserNS()) return; + CHECK(creds.DropFileSystemAccess()); + CHECK(creds.DropAllCapabilities()); + + // The kernel should now prevent us from regaining capabilities because we + // are in a chroot. + CHECK(!creds.MoveToNewUserNS()); +} + +} // namespace. + +} // namespace sandbox. diff --git a/chromium/sandbox/linux/services/init_process_reaper.cc b/chromium/sandbox/linux/services/init_process_reaper.cc new file mode 100644 index 00000000000..2e0b90b7b5d --- /dev/null +++ b/chromium/sandbox/linux/services/init_process_reaper.cc @@ -0,0 +1,101 @@ +// Copyright 2013 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/linux/services/init_process_reaper.h" + +#include +#include +#include +#include +#include +#include + +#include "base/callback.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" + +namespace sandbox { + +namespace { + +void DoNothingSignalHandler(int signal) {} + +} // namespace + +bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) { + int sync_fds[2]; + // We want to use send, so we can't use a pipe + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) { + PLOG(ERROR) << "Failed to create socketpair"; + return false; + } + pid_t child_pid = fork(); + if (child_pid == -1) { + int close_ret; + close_ret = IGNORE_EINTR(close(sync_fds[0])); + DPCHECK(!close_ret); + close_ret = IGNORE_EINTR(close(sync_fds[1])); + DPCHECK(!close_ret); + return false; + } + if (child_pid) { + // In the parent, assuming the role of an init process. + // The disposition for SIGCHLD cannot be SIG_IGN or wait() will only return + // once all of our childs are dead. Since we're init we need to reap childs + // as they come. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = &DoNothingSignalHandler; + CHECK(sigaction(SIGCHLD, &action, NULL) == 0); + + int close_ret; + close_ret = IGNORE_EINTR(close(sync_fds[0])); + DPCHECK(!close_ret); + close_ret = shutdown(sync_fds[1], SHUT_RD); + DPCHECK(!close_ret); + if (post_fork_parent_callback) + post_fork_parent_callback->Run(); + // Tell the child to continue + CHECK(HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) == 1); + close_ret = IGNORE_EINTR(close(sync_fds[1])); + DPCHECK(!close_ret); + + for (;;) { + // Loop until we have reaped our one natural child + siginfo_t reaped_child_info; + int wait_ret = + HANDLE_EINTR(waitid(P_ALL, 0, &reaped_child_info, WEXITED)); + if (wait_ret) + _exit(1); + if (reaped_child_info.si_pid == child_pid) { + int exit_code = 0; + // We're done waiting + if (reaped_child_info.si_code == CLD_EXITED) { + exit_code = reaped_child_info.si_status; + } + // Exit with the same exit code as our parent. Exit with 0 if we got + // signaled. + _exit(exit_code); + } + } + } else { + // The child needs to wait for the parent to run the callback to avoid a + // race condition. + int close_ret; + close_ret = IGNORE_EINTR(close(sync_fds[1])); + DPCHECK(!close_ret); + close_ret = shutdown(sync_fds[0], SHUT_WR); + DPCHECK(!close_ret); + char should_continue; + int read_ret = HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)); + close_ret = IGNORE_EINTR(close(sync_fds[0])); + DPCHECK(!close_ret); + if (read_ret == 1) + return true; + else + return false; + } +} + +} // namespace sandbox. diff --git a/chromium/sandbox/linux/services/init_process_reaper.h b/chromium/sandbox/linux/services/init_process_reaper.h new file mode 100644 index 00000000000..531d18cba11 --- /dev/null +++ b/chromium/sandbox/linux/services/init_process_reaper.h @@ -0,0 +1,23 @@ +// Copyright 2013 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_LINUX_SERVICES_INIT_PROCESS_REAPER_H_ +#define SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_ + +#include "base/callback_forward.h" + +namespace sandbox { + +// The current process will fork(). The parent will become a process reaper +// like init(1). The child will continue normally (after this function +// returns). +// If not NULL, |post_fork_parent_callback| will run in the parent almost +// immediately after fork(). +// Since this function calls fork(), it's very important that the caller has +// only one thread running. +bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback); + +} // namespace sandbox. + +#endif // SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_ diff --git a/chromium/sandbox/linux/services/thread_helpers.cc b/chromium/sandbox/linux/services/thread_helpers.cc new file mode 100644 index 00000000000..e0794f84a44 --- /dev/null +++ b/chromium/sandbox/linux/services/thread_helpers.cc @@ -0,0 +1,84 @@ +// Copyright 2014 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/linux/services/thread_helpers.h" + +#include +#include +#include +#include +#include + +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_number_conversions.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread.h" + +namespace sandbox { + +bool ThreadHelpers::IsSingleThreaded(int proc_self_task) { + CHECK_LE(0, proc_self_task); + struct stat task_stat; + int fstat_ret = fstat(proc_self_task, &task_stat); + PCHECK(0 == fstat_ret); + + // At least "..", "." and the current thread should be present. + CHECK_LE(3UL, task_stat.st_nlink); + // Counting threads via /proc/self/task could be racy. For the purpose of + // determining if the current proces is monothreaded it works: if at any + // time it becomes monothreaded, it'll stay so. + return task_stat.st_nlink == 3; +} + +bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_self_task, + base::Thread* thread) { + DCHECK_LE(0, proc_self_task); + DCHECK(thread); + const base::PlatformThreadId thread_id = thread->thread_id(); + const std::string thread_id_dir_str = base::IntToString(thread_id) + "/"; + + // The kernel is at liberty to wake the thread id futex before updating + // /proc. Following Stop(), the thread is joined, but entries in /proc may + // not have been updated. + thread->Stop(); + + unsigned int iterations = 0; + bool thread_present_in_procfs = true; + // Poll /proc with an exponential back-off, sleeping 2^iterations nanoseconds + // in nanosleep(2). + // Note: the clock may not allow for nanosecond granularity, in this case the + // first iterations would sleep a tiny bit more instead, which would not + // change the calculations significantly. + while (thread_present_in_procfs) { + struct stat task_stat; + const int fstat_ret = + fstatat(proc_self_task, thread_id_dir_str.c_str(), &task_stat, 0); + if (fstat_ret < 0) { + PCHECK(ENOENT == errno); + // The thread disappeared from /proc, we're done. + thread_present_in_procfs = false; + break; + } + // Increase the waiting time exponentially. + struct timespec ts = {0, 1L << iterations /* nanoseconds */}; + PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts))); + ++iterations; + + // Crash after 30 iterations, which means having spent roughly 2s in + // nanosleep(2) cumulatively. + CHECK_GT(30U, iterations); + // In practice, this never goes through more than a couple iterations. In + // debug mode, crash after 64ms (+ eventually 25 times the granularity of + // the clock) in nanosleep(2). + DCHECK_GT(25U, iterations); + } + + return true; +} + +} // namespace sandbox diff --git a/chromium/sandbox/linux/services/thread_helpers.h b/chromium/sandbox/linux/services/thread_helpers.h new file mode 100644 index 00000000000..651e5d9c501 --- /dev/null +++ b/chromium/sandbox/linux/services/thread_helpers.h @@ -0,0 +1,33 @@ +// Copyright 2014 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_LINUX_SERVICES_THREAD_HELPERS_H_ +#define SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_ + +#include "base/basictypes.h" + +namespace base { class Thread; } + +namespace sandbox { + +class ThreadHelpers { + public: + // Check whether the current process is single threaded. |proc_self_tasks| + // should be a file descriptor to /proc/self/task/ and remains owned by the + // caller. + static bool IsSingleThreaded(int proc_self_task); + + // Stop |thread| and ensure that it does not have an entry in + // /proc/self/task/ from the point of view of the current thread. This is + // the way to stop threads before calling IsSingleThreaded(). + static bool StopThreadAndWatchProcFS(int proc_self_tasks, + base::Thread* thread); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadHelpers); +}; + +} // namespace content + +#endif // SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_ diff --git a/chromium/sandbox/linux/services/thread_helpers_unittests.cc b/chromium/sandbox/linux/services/thread_helpers_unittests.cc new file mode 100644 index 00000000000..725a62ef766 --- /dev/null +++ b/chromium/sandbox/linux/services/thread_helpers_unittests.cc @@ -0,0 +1,93 @@ +// Copyright 2014 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/linux/services/thread_helpers.h" + +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/posix/eintr_wrapper.h" +#include "base/process/process_metrics.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::PlatformThread; + +namespace sandbox { + +namespace { + +int GetRaceTestIterations() { + if (IsRunningOnValgrind()) { + return 2; + } else { + return 1000; + } +} + +class ScopedProcSelfTask { + public: + ScopedProcSelfTask() : fd_(-1) { + fd_ = open("/proc/self/task/", O_RDONLY | O_DIRECTORY); + CHECK_LE(0, fd_); + } + + ~ScopedProcSelfTask() { PCHECK(0 == IGNORE_EINTR(close(fd_))); } + + int fd() { return fd_; } + + private: + int fd_; + DISALLOW_COPY_AND_ASSIGN(ScopedProcSelfTask); +}; + +TEST(ThreadHelpers, IsSingleThreadedBasic) { + ScopedProcSelfTask task; + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd())); + + base::Thread thread("sandbox_tests"); + ASSERT_TRUE(thread.Start()); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd())); +} + +TEST(ThreadHelpers, IsSingleThreadedIterated) { + ScopedProcSelfTask task; + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd())); + + // Iterate to check for race conditions. + for (int i = 0; i < GetRaceTestIterations(); ++i) { + base::Thread thread("sandbox_tests"); + ASSERT_TRUE(thread.Start()); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd())); + } +} + +TEST(ThreadHelpers, IsSingleThreadedStartAndStop) { + ScopedProcSelfTask task; + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd())); + + base::Thread thread("sandbox_tests"); + // This is testing for a race condition, so iterate. + // Manually, this has been tested with more that 1M iterations. + for (int i = 0; i < GetRaceTestIterations(); ++i) { + ASSERT_TRUE(thread.Start()); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd())); + + ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread)); + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd())); + ASSERT_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle())); + } +} + +} // namespace + +} // namespace sandbox diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc index 34231d47a1b..740823a8b36 100644 --- a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc +++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc @@ -12,6 +12,7 @@ #include "base/posix/eintr_wrapper.h" #include "base/strings/string_number_conversions.h" +#include "sandbox/linux/services/init_process_reaper.h" #include "sandbox/linux/suid/common/sandbox.h" #include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" #include "setuid_sandbox_client.h" @@ -150,6 +151,11 @@ bool SetuidSandboxClient::ChrootMe() { return true; } +bool SetuidSandboxClient::CreateInitProcessReaper( + base::Closure* post_fork_parent_callback) { + return sandbox::CreateInitProcessReaper(post_fork_parent_callback); +} + bool SetuidSandboxClient::IsSuidSandboxUpToDate() const { return GetHelperApi(env_) == kSUIDSandboxApiNumber; } diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h index a9f6536cc15..5a6724dadd8 100644 --- a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h +++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h @@ -6,6 +6,7 @@ #define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ #include "base/basictypes.h" +#include "base/callback_forward.h" namespace base { class Environment; } @@ -30,6 +31,11 @@ class SetuidSandboxClient { // to an empty directory. // Will only work if we have been launched through the setuid helper. bool ChrootMe(); + // When a new PID namespace is created, the process with pid == 1 should + // assume the role of init. + // See sandbox/linux/services/init_process_reaper.h for more information + // on this API. + bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback); // Did we get launched through an up to date setuid binary ? bool IsSuidSandboxUpToDate() const; diff --git a/chromium/sandbox/linux/suid/common/sandbox.h b/chromium/sandbox/linux/suid/common/sandbox.h index aad4ff8bd30..93452878150 100644 --- a/chromium/sandbox/linux/suid/common/sandbox.h +++ b/chromium/sandbox/linux/suid/common/sandbox.h @@ -12,9 +12,6 @@ namespace sandbox { // These are command line switches that may be used by other programs // (e.g. Chrome) to construct a command line for the sandbox. static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score"; -#if defined(OS_CHROMEOS) -static const char kAdjustLowMemMarginSwitch[] = "--adjust-low-mem"; -#endif static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; diff --git a/chromium/sandbox/linux/suid/process_util_linux.c b/chromium/sandbox/linux/suid/process_util_linux.c index 5e6b33b60f9..78c27ef5075 100644 --- a/chromium/sandbox/linux/suid/process_util_linux.c +++ b/chromium/sandbox/linux/suid/process_util_linux.c @@ -24,10 +24,6 @@ static const int kMaxOomScore = 1000; static const int kMaxOldOomScore = 15; -// Kernel pseudo-file that allows setting of the low memory margin. -static const char kLowMemMarginFile[] = - "/sys/kernel/mm/chromeos-low_mem/margin"; - // NOTE: This is not the only version of this function in the source: // the base library (in process_util_linux.cc) also has its own C++ version. bool AdjustOOMScore(pid_t process, int score) { @@ -77,30 +73,3 @@ bool AdjustOOMScore(pid_t process, int score) { close(fd); return (bytes_written == len); } - -bool AdjustLowMemoryMargin(int64_t margin_mb) { - int file_descriptor = open(kLowMemMarginFile, O_WRONLY); - if (file_descriptor < 0) - return false; - - // Only allow those values which are reasonable, to prevent mischief. - char value[21]; - switch (margin_mb) { - case -1L: - snprintf(value, sizeof(value), "off"); - break; - case 0L: - case 25L: - case 50L: - case 100L: - case 200L: - snprintf(value, sizeof(value), "%lld", (long long int)margin_mb); - break; - default: - return false; - } - - bool success = (write(file_descriptor, value, strlen(value)) >= 0); - close(file_descriptor); - return success; -} diff --git a/chromium/sandbox/linux/suid/sandbox.c b/chromium/sandbox/linux/suid/sandbox.c index 32435a7ad89..238a647abe9 100644 --- a/chromium/sandbox/linux/suid/sandbox.c +++ b/chromium/sandbox/linux/suid/sandbox.c @@ -58,6 +58,15 @@ static void FatalError(const char *msg, ...) { _exit(1); } +static void ExitWithErrorSignalHandler(int signal) { + const char msg[] = "\nThe setuid sandbox got signaled, exiting.\n"; + if (-1 == write(2, msg, sizeof(msg) - 1)) { + // Do nothing. + } + + _exit(1); +} + // We will chroot() to the helper's /proc/self directory. Anything there will // not exist anymore if we make sure to wait() for the helper. // @@ -195,6 +204,15 @@ static void WaitForChildAndExit(pid_t child_pid) { int exit_code = -1; siginfo_t reaped_child_info; + // Don't "Core" on SIGABRT. SIGABRT is sent by the Chrome OS session manager + // when things are hanging. + // Here, the current process is going to waitid() and _exit(), so there is no + // point in generating a crash report. The child process is the one + // blocking us. + if (signal(SIGABRT, ExitWithErrorSignalHandler) == SIG_ERR) { + FatalError("Failed to change signal handler"); + } + int wait_ret = HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED)); @@ -450,16 +468,6 @@ int main(int argc, char **argv) { return 1; return AdjustOOMScore(pid, score); } -#if defined(OS_CHROMEOS) - if (argc == 3 && (0 == strcmp(argv[1], kAdjustLowMemMarginSwitch))) { - char* endptr = NULL; - errno = 0; - unsigned long margin_mb = strtoul(argv[2], &endptr, 10); - if (!endptr || *endptr || errno != 0) - return 1; - return AdjustLowMemoryMargin(margin_mb); - } -#endif // Protect the core setuid sandbox functionality with an API version if (!CheckAndExportApiVersion()) { diff --git a/chromium/sandbox/win/sandbox_poc/main_ui_window.cc b/chromium/sandbox/win/sandbox_poc/main_ui_window.cc index 4d7c13a2663..1671424942c 100644 --- a/chromium/sandbox/win/sandbox_poc/main_ui_window.cc +++ b/chromium/sandbox/win/sandbox_poc/main_ui_window.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2014 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. @@ -174,8 +174,8 @@ INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog, HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); wchar_t current_dir[MAX_PATH]; if (GetCurrentDirectory(MAX_PATH, current_dir)) { - std::wstring dll_path = std::wstring(current_dir) + - std::wstring(kDefaultDll_); + base::string16 dll_path = base::string16(current_dir) + + base::string16(kDefaultDll_); ::SetWindowText(edit_box_dll_name, dll_path.c_str()); } @@ -204,7 +204,7 @@ INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog, return static_cast(TRUE); } else if (LOWORD(wparam) == IDC_BROWSE_DLL) { // If the user presses the Browse button to look for a DLL - std::wstring dll_path = host->OnShowBrowseForDllDlg(dialog); + base::string16 dll_path = host->OnShowBrowseForDllDlg(dialog); if (dll_path.length() > 0) { // Initialize the window text for Log File edit box HWND edit_box_dll_path = ::GetDlgItem(dialog, IDC_DLL_NAME); @@ -213,7 +213,7 @@ INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog, return static_cast(TRUE); } else if (LOWORD(wparam) == IDC_BROWSE_LOG) { // If the user presses the Browse button to look for a log file - std::wstring log_path = host->OnShowBrowseForLogFileDlg(dialog); + base::string16 log_path = host->OnShowBrowseForLogFileDlg(dialog); if (log_path.length() > 0) { // Initialize the window text for Log File edit box HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); @@ -354,7 +354,7 @@ bool MainUIWindow::OnLaunchDll(HWND dialog) { } // store these values in the member variables for use in SpawnTarget - log_file_ = std::wstring(L"\"") + log_file + std::wstring(L"\""); + log_file_ = base::string16(L"\"") + log_file + base::string16(L"\""); dll_path_ = dll_path; entry_point_ = entry_point; @@ -553,8 +553,9 @@ bool MainUIWindow::SpawnTarget() { if (INVALID_HANDLE_VALUE == pipe_handle_) AddDebugMessage(L"Failed to create pipe. Error %d", ::GetLastError()); - if (!sandbox::AddKnownSidToKernelObject(pipe_handle_, WinWorldSid, - FILE_ALL_ACCESS)) + if (!sandbox::AddKnownSidToObject(pipe_handle_, SE_KERNEL_OBJECT, + WinWorldSid, GRANT_ACCESS, + FILE_ALL_ACCESS)) AddDebugMessage(L"Failed to set security on pipe. Error %d", ::GetLastError()); @@ -575,7 +576,7 @@ bool MainUIWindow::SpawnTarget() { return return_value; } -std::wstring MainUIWindow::OnShowBrowseForDllDlg(HWND owner) { +base::string16 MainUIWindow::OnShowBrowseForDllDlg(HWND owner) { wchar_t filename[MAX_PATH]; wcscpy_s(filename, MAX_PATH, L""); @@ -595,7 +596,7 @@ std::wstring MainUIWindow::OnShowBrowseForDllDlg(HWND owner) { return L""; } -std::wstring MainUIWindow::OnShowBrowseForLogFileDlg(HWND owner) { +base::string16 MainUIWindow::OnShowBrowseForLogFileDlg(HWND owner) { wchar_t filename[MAX_PATH]; wcscpy_s(filename, MAX_PATH, L""); diff --git a/chromium/sandbox/win/sandbox_poc/main_ui_window.h b/chromium/sandbox/win/sandbox_poc/main_ui_window.h index c70189abfe7..84fb9861c90 100644 --- a/chromium/sandbox/win/sandbox_poc/main_ui_window.h +++ b/chromium/sandbox/win/sandbox_poc/main_ui_window.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" namespace sandbox { class BrokerServices; @@ -124,11 +125,11 @@ class MainUIWindow { // Shows a standard File Open dialog and returns the DLL filename selected or // blank string if the user cancelled (or an error occurred). - std::wstring OnShowBrowseForDllDlg(HWND owner); + base::string16 OnShowBrowseForDllDlg(HWND owner); // Shows a standard Save As dialog and returns the log filename selected or // blank string if the user cancelled (or an error occurred). - std::wstring OnShowBrowseForLogFileDlg(HWND owner); + base::string16 OnShowBrowseForLogFileDlg(HWND owner); // Formats a message using the supplied format string and prints it in the // listview in the main UI window. Passing a NULL param in 'fmt' results in @@ -164,20 +165,20 @@ class MainUIWindow { // This is essentially a command line to a target executable that the // broker will spawn and ask to load the DLL. - std::wstring spawn_target_; + base::string16 spawn_target_; // A handle to the current instance of the app. Passed in to this class // through CreateMainWindowAndLoop. HINSTANCE instance_handle_; // A path to the DLL that the target should load once it executes. - std::wstring dll_path_; + base::string16 dll_path_; // The name of the entry point the target should call after it loads the DLL. - std::wstring entry_point_; + base::string16 entry_point_; // The name of the log file to use. - std::wstring log_file_; + base::string16 log_file_; // This is a static handle to the list view that fills up the entire main // UI window. The list view is used to display debugging information to the diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc b/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc index a77fe0a5809..cd75d4f2412 100644 --- a/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc +++ b/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include + +#include "base/strings/string16.h" #include "sandbox/win/sandbox_poc/pocdll/exports.h" #include "sandbox/win/sandbox_poc/pocdll/utils.h" @@ -23,7 +25,7 @@ void POCDLL_API TestSpyKeys(HANDLE log) { fprintf(output, "[INFO] Logging keystrokes for 15 seconds\r\n"); fflush(output); - std::wstring logged; + base::string16 logged; DWORD tick = ::GetTickCount() + 15000; while (tick > ::GetTickCount()) { for (int i = 0; i < 256; ++i) { diff --git a/chromium/sandbox/win/sandbox_poc/sandbox.cc b/chromium/sandbox/win/sandbox_poc/sandbox.cc index 31cf3083c84..e2820753211 100644 --- a/chromium/sandbox/win/sandbox_poc/sandbox.cc +++ b/chromium/sandbox/win/sandbox_poc/sandbox.cc @@ -17,7 +17,7 @@ typedef void(__cdecl *lpfnInit)(HANDLE); bool ParseCommandLine(wchar_t * command_line, std::string * dll_name, std::string * entry_point, - std::wstring * log_file) { + base::string16 * log_file) { DCHECK(dll_name); DCHECK(entry_point); DCHECK(log_file); @@ -35,8 +35,8 @@ bool ParseCommandLine(wchar_t * command_line, return false; } - std::wstring entry_point_wide = arg_list[1]; - std::wstring dll_name_wide = arg_list[2]; + base::string16 entry_point_wide = arg_list[1]; + base::string16 dll_name_wide = arg_list[2]; *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end()); *dll_name = std::string(dll_name_wide.begin(), dll_name_wide.end()); *log_file = arg_list[3]; @@ -126,7 +126,7 @@ int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, wchar_t* command_line, // Parse the command line to find out what we need to call std::string dll_name, entry_point; - std::wstring log_file; + base::string16 log_file; if (!ParseCommandLine(GetCommandLineW(), &dll_name, &entry_point, diff --git a/chromium/sandbox/win/sandbox_win.gypi b/chromium/sandbox/win/sandbox_win.gypi index ab2be495fd9..1fa82794bbf 100644 --- a/chromium/sandbox/win/sandbox_win.gypi +++ b/chromium/sandbox/win/sandbox_win.gypi @@ -93,6 +93,7 @@ 'src/restricted_token.cc', 'src/restricted_token.h', 'src/sandbox_factory.h', + 'src/sandbox_globals.cc', 'src/sandbox_nt_types.h', 'src/sandbox_nt_util.cc', 'src/sandbox_nt_util.h', @@ -336,7 +337,6 @@ 'target_arch': 'x64', }, 'dependencies': [ - '../testing/gtest.gyp:gtest', '../base/base.gyp:base_nacl_win64', '../base/base.gyp:base_static_win64', ], diff --git a/chromium/sandbox/win/src/Wow64.cc b/chromium/sandbox/win/src/Wow64.cc index 39108e50b36..b11026b1a52 100644 --- a/chromium/sandbox/win/src/Wow64.cc +++ b/chromium/sandbox/win/src/Wow64.cc @@ -142,13 +142,13 @@ bool Wow64::RunWowHelper(void* buffer) { // Get the path to the helper (beside the exe). wchar_t prog_name[MAX_PATH]; GetModuleFileNameW(NULL, prog_name, MAX_PATH); - std::wstring path(prog_name); + base::string16 path(prog_name); size_t name_pos = path.find_last_of(L"\\"); - if (std::wstring::npos == name_pos) + if (base::string16::npos == name_pos) return false; path.resize(name_pos + 1); - std::wstringstream command; + std::basic_stringstream command; command << std::hex << std::showbase << L"\"" << path << L"wow_helper.exe\" " << child_->ProcessId() << " " << bit_cast(buffer); @@ -157,10 +157,11 @@ bool Wow64::RunWowHelper(void* buffer) { STARTUPINFO startup_info = {0}; startup_info.cb = sizeof(startup_info); - base::win::ScopedProcessInformation process_info; + PROCESS_INFORMATION temp_process_info = {}; if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL, - NULL, &startup_info, process_info.Receive())) + NULL, &startup_info, &temp_process_info)) return false; + base::win::ScopedProcessInformation process_info(temp_process_info); DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE); diff --git a/chromium/sandbox/win/src/acl.cc b/chromium/sandbox/win/src/acl.cc index 70d2a8d3109..bf59d962a54 100644 --- a/chromium/sandbox/win/src/acl.cc +++ b/chromium/sandbox/win/src/acl.cc @@ -36,10 +36,10 @@ bool GetDefaultDacl(HANDLE token, return true; } -bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access, - ACL** new_dacl) { +bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MODE access_mode, + ACCESS_MASK access, ACL** new_dacl) { EXPLICIT_ACCESS new_access = {0}; - new_access.grfAccessMode = GRANT_ACCESS; + new_access.grfAccessMode = access_mode; new_access.grfAccessPermissions = access; new_access.grfInheritance = NO_INHERITANCE; @@ -64,7 +64,8 @@ bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) { return false; ACL* new_dacl = NULL; - if (!AddSidToDacl(sid, default_dacl->DefaultDacl, access, &new_dacl)) + if (!AddSidToDacl(sid, default_dacl->DefaultDacl, GRANT_ACCESS, access, + &new_dacl)) return false; TOKEN_DEFAULT_DACL new_token_dacl = {0}; @@ -90,23 +91,24 @@ bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) { access); } -bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid, - ACCESS_MASK access) { +bool AddKnownSidToObject(HANDLE object, SE_OBJECT_TYPE object_type, + const Sid& sid, ACCESS_MODE access_mode, + ACCESS_MASK access) { PSECURITY_DESCRIPTOR descriptor = NULL; PACL old_dacl = NULL; PACL new_dacl = NULL; - if (ERROR_SUCCESS != ::GetSecurityInfo(object, SE_KERNEL_OBJECT, + if (ERROR_SUCCESS != ::GetSecurityInfo(object, object_type, DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, NULL, &descriptor)) return false; - if (!AddSidToDacl(sid.GetPSID(), old_dacl, access, &new_dacl)) { + if (!AddSidToDacl(sid.GetPSID(), old_dacl, access_mode, access, &new_dacl)) { ::LocalFree(descriptor); return false; } - DWORD result = ::SetSecurityInfo(object, SE_KERNEL_OBJECT, + DWORD result = ::SetSecurityInfo(object, object_type, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL); diff --git a/chromium/sandbox/win/src/acl.h b/chromium/sandbox/win/src/acl.h index 25d5cdb1611..531259fbebd 100644 --- a/chromium/sandbox/win/src/acl.h +++ b/chromium/sandbox/win/src/acl.h @@ -5,6 +5,7 @@ #ifndef SANDBOX_SRC_ACL_H_ #define SANDBOX_SRC_ACL_H_ +#include #include #include "base/memory/scoped_ptr.h" @@ -16,11 +17,11 @@ namespace sandbox { bool GetDefaultDacl(HANDLE token, scoped_ptr_malloc* default_dacl); -// Appends an ACE represented by |sid| and |access| to |old_dacl|. If the -// function succeeds, new_dacl contains the new dacl and must be freed using -// LocalFree. -bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access, - ACL** new_dacl); +// Appends an ACE represented by |sid|, |access_mode|, and |access| to +// |old_dacl|. If the function succeeds, new_dacl contains the new dacl and +// must be freed using LocalFree. +bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MODE access_mode, + ACCESS_MASK access, ACL** new_dacl); // Adds and ACE represented by |sid| and |access| to the default dacl present // in the token. @@ -30,10 +31,11 @@ bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access); // present in the token. bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access); -// Adds an ACE represented by |known_sid| and |access| to the dacl of the kernel -// object referenced by |object|. -bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid, - ACCESS_MASK access); +// Adds an ACE represented by |known_sid|, |access_mode|, and |access| to +// the dacl of the kernel object referenced by |object| and of |object_type|. +bool AddKnownSidToObject(HANDLE object, SE_OBJECT_TYPE object_type, + const Sid& sid, ACCESS_MODE access_mode, + ACCESS_MASK access); } // namespace sandbox diff --git a/chromium/sandbox/win/src/crosscall_params.h b/chromium/sandbox/win/src/crosscall_params.h index c5298ba7bec..cc82c160fde 100644 --- a/chromium/sandbox/win/src/crosscall_params.h +++ b/chromium/sandbox/win/src/crosscall_params.h @@ -235,7 +235,8 @@ class ActualCallParams : public CrossCallParams { return false; } - if (param_info_[index].offset_ > sizeof(*this)) { + if ((size > sizeof(*this)) || + (param_info_[index].offset_ > (sizeof(*this) - size))) { // It does not fit, abort copy. return false; } diff --git a/chromium/sandbox/win/src/crosscall_server.cc b/chromium/sandbox/win/src/crosscall_server.cc index fea855337d2..ab8b42113fa 100644 --- a/chromium/sandbox/win/src/crosscall_server.cc +++ b/chromium/sandbox/win/src/crosscall_server.cc @@ -237,7 +237,7 @@ bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) { // Covers the common case of reading a string. Note that the string is not // scanned for invalid characters. -bool CrossCallParamsEx::GetParameterStr(uint32 index, std::wstring* string) { +bool CrossCallParamsEx::GetParameterStr(uint32 index, base::string16* string) { uint32 size = 0; ArgType type; void* start = GetRawParameter(index, &size, &type); diff --git a/chromium/sandbox/win/src/crosscall_server.h b/chromium/sandbox/win/src/crosscall_server.h index 2a395077db7..e7540065412 100644 --- a/chromium/sandbox/win/src/crosscall_server.h +++ b/chromium/sandbox/win/src/crosscall_server.h @@ -9,6 +9,7 @@ #include #include "base/basictypes.h" #include "base/callback.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_params.h" // This is the IPC server interface for CrossCall: The IPC for the Sandbox @@ -112,7 +113,7 @@ class CrossCallParamsEx : public CrossCallParams { // Gets a parameter that is a string. Returns false if the parameter does not // exist. - bool GetParameterStr(uint32 index, std::wstring* string); + bool GetParameterStr(uint32 index, base::string16* string); // Gets a parameter that is an in/out buffer. Returns false is the parameter // does not exist or if the size of the actual parameter is not equal to the diff --git a/chromium/sandbox/win/src/file_policy_test.cc b/chromium/sandbox/win/src/file_policy_test.cc index 85aea0b7071..adda1a5f874 100644 --- a/chromium/sandbox/win/src/file_policy_test.cc +++ b/chromium/sandbox/win/src/file_policy_test.cc @@ -62,7 +62,7 @@ SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } - std::wstring full_path = MakePathToSys(argv[0], false); + base::string16 full_path = MakePathToSys(argv[0], false); if (full_path.empty()) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } @@ -94,7 +94,7 @@ SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { if (argc != 1) return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - std::wstring file(argv[0]); + base::string16 file(argv[0]); if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) file = MakePathToSys(argv[0], true); @@ -132,7 +132,7 @@ SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { if (argc != 1) return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - std::wstring file = MakePathToSys(argv[0], true); + base::string16 file = MakePathToSys(argv[0], true); UNICODE_STRING object_name; RtlInitUnicodeString(&object_name, file.c_str()); @@ -156,7 +156,7 @@ SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { } SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { - std::wstring sys_path = MakePathToSys(L"", false); + base::string16 sys_path = MakePathToSys(L"", false); if (sys_path.empty()) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } @@ -212,7 +212,7 @@ SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) { bool expect_directory = (L'd' == argv[1][0]); UNICODE_STRING object_name; - std::wstring file = MakePathToSys(argv[0], true); + base::string16 file = MakePathToSys(argv[0], true); RtlInitUnicodeString(&object_name, file.c_str()); OBJECT_ATTRIBUTES obj_attributes = {0}; @@ -265,8 +265,8 @@ TEST(FilePolicyTest, AllowNtCreateCalc) { } TEST(FilePolicyTest, AllowNtCreateWithNativePath) { - std::wstring calc = MakePathToSys(L"calc.exe", false); - std::wstring nt_path; + base::string16 calc = MakePathToSys(L"calc.exe", false); + base::string16 nt_path; ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); TestRunner runner; runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); @@ -533,9 +533,9 @@ TEST(FilePolicyTest, DISABLED_TestReparsePoint) { ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL)); // Create a temporary file in the subfolder. - std::wstring subfolder = temp_file_name; - std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); - std::wstring temp_file = subfolder + L"\\file_" + temp_file_title; + base::string16 subfolder = temp_file_name; + base::string16 temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); + base::string16 temp_file = subfolder + L"\\file_" + temp_file_title; HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, @@ -544,8 +544,8 @@ TEST(FilePolicyTest, DISABLED_TestReparsePoint) { ASSERT_TRUE(::CloseHandle(file)); // Create a temporary file in the temp directory. - std::wstring temp_dir = temp_directory; - std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title; + base::string16 temp_dir = temp_directory; + base::string16 temp_file_in_temp = temp_dir + L"file_" + temp_file_title; file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); @@ -553,12 +553,12 @@ TEST(FilePolicyTest, DISABLED_TestReparsePoint) { ASSERT_TRUE(::CloseHandle(file)); // Give write access to the temp directory. - std::wstring temp_dir_wildcard = temp_dir + L"*"; + base::string16 temp_dir_wildcard = temp_dir + L"*"; EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_dir_wildcard.c_str())); // Prepare the command to execute. - std::wstring command_write; + base::string16 command_write; command_write += L"File_Create Write \""; command_write += temp_file; command_write += L"\""; @@ -573,7 +573,7 @@ TEST(FilePolicyTest, DISABLED_TestReparsePoint) { OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); - std::wstring temp_dir_nt; + base::string16 temp_dir_nt; temp_dir_nt += L"\\??\\"; temp_dir_nt += temp_dir; EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); diff --git a/chromium/sandbox/win/src/filesystem_dispatcher.cc b/chromium/sandbox/win/src/filesystem_dispatcher.cc index 22240ff0e55..275122be918 100644 --- a/chromium/sandbox/win/src/filesystem_dispatcher.cc +++ b/chromium/sandbox/win/src/filesystem_dispatcher.cc @@ -83,7 +83,7 @@ bool FilesystemDispatcher::SetupService(InterceptionManager* manager, } bool FilesystemDispatcher::NtCreateFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access, + IPCInfo* ipc, base::string16* name, DWORD attributes, DWORD desired_access, DWORD file_attributes, DWORD share_access, DWORD create_disposition, DWORD create_options) { if (!PreProcessName(*name, name)) { @@ -126,7 +126,7 @@ bool FilesystemDispatcher::NtCreateFile( } bool FilesystemDispatcher::NtOpenFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access, + IPCInfo* ipc, base::string16* name, DWORD attributes, DWORD desired_access, DWORD share_access, DWORD open_options) { if (!PreProcessName(*name, name)) { // The path requested might contain a reparse point. @@ -166,7 +166,7 @@ bool FilesystemDispatcher::NtOpenFile( } bool FilesystemDispatcher::NtQueryAttributesFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) { + IPCInfo* ipc, base::string16* name, DWORD attributes, CountedBuffer* info) { if (sizeof(FILE_BASIC_INFORMATION) != info->Size()) return false; @@ -204,7 +204,7 @@ bool FilesystemDispatcher::NtQueryAttributesFile( } bool FilesystemDispatcher::NtQueryFullAttributesFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) { + IPCInfo* ipc, base::string16* name, DWORD attributes, CountedBuffer* info) { if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size()) return false; @@ -257,7 +257,7 @@ bool FilesystemDispatcher::NtSetInformationFile( if (!IsSupportedRenameCall(rename_info, length, info_class)) return false; - std::wstring name; + base::string16 name; name.assign(rename_info->FileName, rename_info->FileNameLength / sizeof(rename_info->FileName[0])); if (!PreProcessName(name, &name)) { diff --git a/chromium/sandbox/win/src/filesystem_dispatcher.h b/chromium/sandbox/win/src/filesystem_dispatcher.h index b0d9a7a0a95..257e4f7e274 100644 --- a/chromium/sandbox/win/src/filesystem_dispatcher.h +++ b/chromium/sandbox/win/src/filesystem_dispatcher.h @@ -6,6 +6,7 @@ #define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -22,29 +23,31 @@ class FilesystemDispatcher : public Dispatcher { private: // Processes IPC requests coming from calls to NtCreateFile in the target. - bool NtCreateFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, + bool NtCreateFile(IPCInfo* ipc, base::string16* name, DWORD attributes, DWORD desired_access, DWORD file_attributes, DWORD share_access, DWORD create_disposition, DWORD create_options); // Processes IPC requests coming from calls to NtOpenFile in the target. - bool NtOpenFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, + bool NtOpenFile(IPCInfo* ipc, base::string16* name, DWORD attributes, DWORD desired_access, DWORD share_access, DWORD create_options); // Processes IPC requests coming from calls to NtQueryAttributesFile in the // target. - bool NtQueryAttributesFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, + bool NtQueryAttributesFile(IPCInfo* ipc, base::string16* name, + DWORD attributes, CountedBuffer* info); // Processes IPC requests coming from calls to NtQueryFullAttributesFile in // the target. - bool NtQueryFullAttributesFile(IPCInfo* ipc, std::wstring* name, + bool NtQueryFullAttributesFile(IPCInfo* ipc, base::string16* name, DWORD attributes, CountedBuffer* info); // Processes IPC requests coming from calls to NtSetInformationFile with the // rename information class. - bool NtSetInformationFile(IPCInfo* ipc, HANDLE handle, CountedBuffer* status, + bool NtSetInformationFile(IPCInfo* ipc, HANDLE handle, + CountedBuffer* status, CountedBuffer* info, DWORD length, DWORD info_class); diff --git a/chromium/sandbox/win/src/filesystem_policy.cc b/chromium/sandbox/win/src/filesystem_policy.cc index 02707b0a847..331b9fbe279 100644 --- a/chromium/sandbox/win/src/filesystem_policy.cc +++ b/chromium/sandbox/win/src/filesystem_policy.cc @@ -61,7 +61,7 @@ namespace sandbox { bool FileSystemPolicy::GenerateRules(const wchar_t* name, TargetPolicy::Semantics semantics, LowLevelPolicy* policy) { - std::wstring mod_name(name); + base::string16 mod_name(name); if (mod_name.empty()) { return false; } @@ -229,7 +229,7 @@ bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) { bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, uint32 desired_access, uint32 file_attributes, @@ -260,7 +260,7 @@ bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, uint32 desired_access, uint32 share_access, @@ -292,7 +292,7 @@ bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, bool FileSystemPolicy::QueryAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, FILE_BASIC_INFORMATION* file_info, NTSTATUS* nt_status) { @@ -317,7 +317,7 @@ bool FileSystemPolicy::QueryAttributesFileAction( bool FileSystemPolicy::QueryFullAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, FILE_NETWORK_OPEN_INFORMATION* file_info, NTSTATUS* nt_status) { @@ -372,7 +372,7 @@ bool FileSystemPolicy::SetInformationFileAction( return true; } -bool PreProcessName(const std::wstring& path, std::wstring* new_path) { +bool PreProcessName(const base::string16& path, base::string16* new_path) { ConvertToLongPath(path, new_path); bool reparsed = false; diff --git a/chromium/sandbox/win/src/filesystem_policy.h b/chromium/sandbox/win/src/filesystem_policy.h index bcedb6356c8..68dffec34ff 100644 --- a/chromium/sandbox/win/src/filesystem_policy.h +++ b/chromium/sandbox/win/src/filesystem_policy.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/policy_low_level.h" @@ -39,7 +40,7 @@ class FileSystemPolicy { // 'file' : The target file or directory. static bool CreateFileAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, uint32 desired_access, uint32 file_attributes, @@ -57,7 +58,7 @@ class FileSystemPolicy { // 'file' : The target file or directory. static bool OpenFileAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, uint32 desired_access, uint32 share_access, @@ -70,7 +71,7 @@ class FileSystemPolicy { // API that is compatible with the IPC-received parameters. static bool QueryAttributesFileAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, FILE_BASIC_INFORMATION* file_info, NTSTATUS* nt_status); @@ -80,7 +81,7 @@ class FileSystemPolicy { static bool QueryFullAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &file, + const base::string16 &file, uint32 attributes, FILE_NETWORK_OPEN_INFORMATION* file_info, NTSTATUS* nt_status); @@ -100,7 +101,7 @@ class FileSystemPolicy { // Expands the path and check if it's a reparse point. Returns false if // we cannot determine or if there is an unexpected error. In that case // the path cannot be trusted. -bool PreProcessName(const std::wstring& path, std::wstring* new_path); +bool PreProcessName(const base::string16& path, base::string16* new_path); } // namespace sandbox diff --git a/chromium/sandbox/win/src/handle_policy_test.cc b/chromium/sandbox/win/src/handle_policy_test.cc index 11e888abe72..99b1717c2c3 100644 --- a/chromium/sandbox/win/src/handle_policy_test.cc +++ b/chromium/sandbox/win/src/handle_policy_test.cc @@ -54,8 +54,8 @@ TEST(HandlePolicyTest, DuplicateHandle) { EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000")); // First test that we fail to open the event. - std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", - target.process_id()); + base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", + target.process_id()); EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); // Now successfully open the event after adding a duplicate handle rule. @@ -76,8 +76,8 @@ TEST(HandlePolicyTest, DuplicatePeerHandle) { EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000")); // First test that we fail to open the event. - std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", - target.process_id()); + base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", + target.process_id()); EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); // Now successfully open the event after adding a duplicate handle rule. @@ -92,7 +92,7 @@ TEST(HandlePolicyTest, DuplicateBrokerHandle) { TestRunner runner; // First test that we fail to open the event. - std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", + base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", ::GetCurrentProcessId()); EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); diff --git a/chromium/sandbox/win/src/interception.cc b/chromium/sandbox/win/src/interception.cc index 8c897a1a977..dde585735f1 100644 --- a/chromium/sandbox/win/src/interception.cc +++ b/chromium/sandbox/win/src/interception.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" #include "base/win/pe_image.h" #include "base/win/windows_version.h" #include "sandbox/win/src/interception_internal.h" @@ -141,7 +142,7 @@ bool InterceptionManager::InitializeInterceptions() { } size_t InterceptionManager::GetBufferSize() const { - std::set dlls; + std::set dlls; size_t buffer_bytes = 0; std::list::const_iterator it = interceptions_.begin(); @@ -202,7 +203,7 @@ bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) { continue; } - const std::wstring dll = it->dll; + const base::string16 dll = it->dll; if (!SetupDllInfo(*it, &buffer, &buffer_bytes)) return false; @@ -355,7 +356,7 @@ bool InterceptionManager::IsInterceptionPerformedByChild( if (data.type >= INTERCEPTION_LAST) return false; - std::wstring ntdll(kNtdllName); + base::string16 ntdll(kNtdllName); if (ntdll == data.dll) return false; // ntdll has to be intercepted from the parent @@ -493,7 +494,7 @@ bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks, std::list::iterator it = interceptions_.begin(); for (; it != interceptions_.end(); ++it) { - const std::wstring ntdll(kNtdllName); + const base::string16 ntdll(kNtdllName); if (it->dll != ntdll) break; diff --git a/chromium/sandbox/win/src/interception.h b/chromium/sandbox/win/src/interception.h index 02fc592a067..739c81645df 100644 --- a/chromium/sandbox/win/src/interception.h +++ b/chromium/sandbox/win/src/interception.h @@ -14,6 +14,7 @@ #include "base/basictypes.h" #include "base/gtest_prod_util.h" +#include "base/strings/string16.h" #include "sandbox/win/src/sandbox_types.h" namespace sandbox { @@ -135,7 +136,7 @@ class InterceptionManager { struct InterceptionData { InterceptionType type; // Interception type. InterceptorId id; // Interceptor id. - std::wstring dll; // Name of dll to intercept. + base::string16 dll; // Name of dll to intercept. std::string function; // Name of function to intercept. std::string interceptor; // Name of interceptor function. const void* interceptor_address; // Interceptor's entry point. @@ -241,6 +242,10 @@ class InterceptionManager { ((&Target##service) ? \ manager->ADD_NT_INTERCEPTION(service, id, num_params) : false) +// When intercepting the EAT it is important that the patched version of the +// function not call any functions imported from system libraries unless +// |TargetServices::InitCalled()| returns true, because it is only then that +// we are guaranteed that our IAT has been initialized. #define INTERCEPT_EAT(manager, dll, function, id, num_params) \ ((&Target##function) ? \ manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ @@ -262,6 +267,10 @@ class InterceptionManager { #define INTERCEPT_NT(manager, service, id, num_params) \ manager->ADD_NT_INTERCEPTION(service, id, num_params) +// When intercepting the EAT it is important that the patched version of the +// function not call any functions imported from system libraries unless +// |TargetServices::InitCalled()| returns true, because it is only then that +// we are guaranteed that our IAT has been initialized. #define INTERCEPT_EAT(manager, dll, function, id, num_params) \ manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ MAKE_SERVICE_NAME(function), id) diff --git a/chromium/sandbox/win/src/interceptors_64.cc b/chromium/sandbox/win/src/interceptors_64.cc index 30843fdd889..c71d5a2803d 100644 --- a/chromium/sandbox/win/src/interceptors_64.cc +++ b/chromium/sandbox/win/src/interceptors_64.cc @@ -249,20 +249,23 @@ SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64( // ----------------------------------------------------------------------- -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64( - LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, - BOOL initial_state, LPCWSTR name) { - CreateEventWFunction orig_fn = reinterpret_cast< - CreateEventWFunction>(g_originals[CREATE_EVENT_ID]); - return TargetCreateEventW(orig_fn, security_attributes, manual_reset, - initial_state, name); +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateEvent64( + PHANDLE event_handle, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, EVENT_TYPE event_type, + BOOLEAN initial_state) { + NtCreateEventFunction orig_fn = reinterpret_cast< + NtCreateEventFunction>(g_originals[CREATE_EVENT_ID]); + return TargetNtCreateEvent(orig_fn, event_handle, desired_access, + object_attributes, event_type, initial_state); } -SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64( - ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name) { - OpenEventWFunction orig_fn = reinterpret_cast< - OpenEventWFunction>(g_originals[OPEN_EVENT_ID]); - return TargetOpenEventW(orig_fn, desired_access, inherit_handle, name); +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent64( + PHANDLE event_handle, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes) { + NtOpenEventFunction orig_fn = reinterpret_cast< + NtOpenEventFunction>(g_originals[OPEN_EVENT_ID]); + return TargetNtOpenEvent(orig_fn, event_handle, desired_access, + object_attributes); } } // namespace sandbox diff --git a/chromium/sandbox/win/src/interceptors_64.h b/chromium/sandbox/win/src/interceptors_64.h index 87c1c507598..ef2c10d412c 100644 --- a/chromium/sandbox/win/src/interceptors_64.h +++ b/chromium/sandbox/win/src/interceptors_64.h @@ -153,14 +153,15 @@ SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64( // ----------------------------------------------------------------------- // Interceptors handled by the sync dispatcher. -// Interception of CreateEventW on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64( - LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, - BOOL initial_state, LPCWSTR name); - -// Interception of OpenEventW on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64( - ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name); +// Interception of NtCreateEvent/NtOpenEvent on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateEvent64( + PHANDLE event_handle, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, EVENT_TYPE event_type, + BOOLEAN initial_state); + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent64( + PHANDLE event_handle, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes); } // extern "C" diff --git a/chromium/sandbox/win/src/ipc_unittest.cc b/chromium/sandbox/win/src/ipc_unittest.cc index daca7bbe4f1..53b870c3f15 100644 --- a/chromium/sandbox/win/src/ipc_unittest.cc +++ b/chromium/sandbox/win/src/ipc_unittest.cc @@ -160,7 +160,7 @@ TEST(IPCTest, CrossCallStrPacking) { CrossCallReturn answer; uint32 tag1 = 666; const wchar_t text[] = L"98765 - 43210"; - std::wstring copied_text; + base::string16 copied_text; CrossCallParamsEx* actual_params; CrossCall(client, tag1, text, &answer); @@ -204,7 +204,7 @@ TEST(IPCTest, CrossCallStrPacking) { EXPECT_STREQ(text, copied_text.c_str()); param_size = 1; - std::wstring copied_text_p0, copied_text_p2; + base::string16 copied_text_p0, copied_text_p2; const wchar_t text2[] = L"AeFG"; CrossCall(client, tag1, text2, null_text, text, &answer); diff --git a/chromium/sandbox/win/src/job_unittest.cc b/chromium/sandbox/win/src/job_unittest.cc index 597c86c1ef3..3b2b331046c 100644 --- a/chromium/sandbox/win/src/job_unittest.cc +++ b/chromium/sandbox/win/src/job_unittest.cc @@ -164,10 +164,11 @@ TEST(JobTest, ProcessInJob) { wchar_t notepad[] = L"notepad"; STARTUPINFO si = { sizeof(si) }; - base::win::ScopedProcessInformation pi; + PROCESS_INFORMATION temp_process_info = {}; result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si, - pi.Receive()); + &temp_process_info); ASSERT_TRUE(result); + base::win::ScopedProcessInformation pi(temp_process_info); ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle())); // Get the job handle. diff --git a/chromium/sandbox/win/src/named_pipe_dispatcher.cc b/chromium/sandbox/win/src/named_pipe_dispatcher.cc index aae1096b27d..da4045c2745 100644 --- a/chromium/sandbox/win/src/named_pipe_dispatcher.cc +++ b/chromium/sandbox/win/src/named_pipe_dispatcher.cc @@ -1,10 +1,11 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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/named_pipe_dispatcher.h" #include "base/basictypes.h" +#include "base/strings/string_split.h" #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/interception.h" @@ -33,16 +34,33 @@ NamedPipeDispatcher::NamedPipeDispatcher(PolicyBase* policy_base) bool NamedPipeDispatcher::SetupService(InterceptionManager* manager, int service) { if (IPC_CREATENAMEDPIPEW_TAG == service) - return INTERCEPT_EAT(manager, L"kernel32.dll", CreateNamedPipeW, + return INTERCEPT_EAT(manager, kKerneldllName, CreateNamedPipeW, CREATE_NAMED_PIPE_ID, 36); return false; } bool NamedPipeDispatcher::CreateNamedPipe( - IPCInfo* ipc, std::wstring* name, DWORD open_mode, DWORD pipe_mode, + IPCInfo* ipc, base::string16* name, DWORD open_mode, DWORD pipe_mode, DWORD max_instances, DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout) { + ipc->return_info.win32_result = ERROR_ACCESS_DENIED; + ipc->return_info.handle = INVALID_HANDLE_VALUE; + + std::vector paths; + std::vector innerpaths; + base::SplitString(*name, '/', &paths); + + for (std::vector::const_iterator iter = paths.begin(); + iter != paths.end(); ++iter) { + base::SplitString(*iter, '\\', &innerpaths); + for (std::vector::const_iterator iter2 = innerpaths.begin(); + iter2 != innerpaths.end(); ++iter2) { + if (*iter2 == L"..") + return true; + } + } + const wchar_t* pipe_name = name->c_str(); CountedParameterSet params; params[NameBased::NAME] = ParamPickerMake(pipe_name); @@ -50,6 +68,16 @@ bool NamedPipeDispatcher::CreateNamedPipe( EvalResult eval = policy_base_->EvalPolicy(IPC_CREATENAMEDPIPEW_TAG, params.GetBase()); + // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to + // disable all string parsing and to send the string that follows it straight + // to the file system." + // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx + // This ensures even if there is a path traversal in the pipe name, and it is + // able to get past the checks above, it will still not be allowed to escape + // our whitelisted namespace. + if (name->compare(0, 4, L"\\\\.\\") == 0) + name->replace(0, 4, L"\\\\\?\\"); + HANDLE pipe; DWORD ret = NamedPipePolicy::CreateNamedPipeAction(eval, *ipc->client_info, *name, open_mode, diff --git a/chromium/sandbox/win/src/named_pipe_dispatcher.h b/chromium/sandbox/win/src/named_pipe_dispatcher.h index 0d03b2e9db5..0707284d6ef 100644 --- a/chromium/sandbox/win/src/named_pipe_dispatcher.h +++ b/chromium/sandbox/win/src/named_pipe_dispatcher.h @@ -6,6 +6,7 @@ #define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -23,7 +24,7 @@ class NamedPipeDispatcher : public Dispatcher { private: // Processes IPC requests coming from calls to CreateNamedPipeW() in the // target. - bool CreateNamedPipe(IPCInfo* ipc, std::wstring* name, DWORD open_mode, + bool CreateNamedPipe(IPCInfo* ipc, base::string16* name, DWORD open_mode, DWORD pipe_mode, DWORD max_instances, DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout); diff --git a/chromium/sandbox/win/src/named_pipe_interception.cc b/chromium/sandbox/win/src/named_pipe_interception.cc index f437b1c2e85..c62d0931d73 100644 --- a/chromium/sandbox/win/src/named_pipe_interception.cc +++ b/chromium/sandbox/win/src/named_pipe_interception.cc @@ -27,12 +27,12 @@ HANDLE WINAPI TargetCreateNamedPipeW( if (INVALID_HANDLE_VALUE != pipe) return pipe; - DWORD original_error = ::GetLastError(); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return INVALID_HANDLE_VALUE; + DWORD original_error = ::GetLastError(); + // We don't support specific Security Attributes. if (security_attributes) return INVALID_HANDLE_VALUE; diff --git a/chromium/sandbox/win/src/named_pipe_policy.cc b/chromium/sandbox/win/src/named_pipe_policy.cc index 0f620b18110..eee719e50aa 100644 --- a/chromium/sandbox/win/src/named_pipe_policy.cc +++ b/chromium/sandbox/win/src/named_pipe_policy.cc @@ -60,7 +60,7 @@ bool NamedPipePolicy::GenerateRules(const wchar_t* name, DWORD NamedPipePolicy::CreateNamedPipeAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &name, + const base::string16 &name, DWORD open_mode, DWORD pipe_mode, DWORD max_instances, DWORD out_buffer_size, diff --git a/chromium/sandbox/win/src/named_pipe_policy.h b/chromium/sandbox/win/src/named_pipe_policy.h index 1ba07b8ba30..c904aa3618b 100644 --- a/chromium/sandbox/win/src/named_pipe_policy.h +++ b/chromium/sandbox/win/src/named_pipe_policy.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/policy_low_level.h" #include "sandbox/win/src/sandbox_policy.h" @@ -31,7 +32,7 @@ class NamedPipePolicy { // Processes a 'CreateNamedPipeW()' request from the target. static DWORD CreateNamedPipeAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &name, + const base::string16 &name, DWORD open_mode, DWORD pipe_mode, DWORD max_instances, DWORD out_buffer_size, diff --git a/chromium/sandbox/win/src/named_pipe_policy_test.cc b/chromium/sandbox/win/src/named_pipe_policy_test.cc index b89a1913d06..fe8c71f70b9 100644 --- a/chromium/sandbox/win/src/named_pipe_policy_test.cc +++ b/chromium/sandbox/win/src/named_pipe_policy_test.cc @@ -1,18 +1,20 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2014 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 "testing/gtest/include/gtest/gtest.h" +#include "base/win/windows_version.h" +#include "sandbox/win/src/handle_closer.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" namespace sandbox { SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) { - if (argc != 1) { + if (argc < 1 || argc > 2) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } if ((NULL == argv) || (NULL == argv[0])) { @@ -26,6 +28,18 @@ SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) { if (INVALID_HANDLE_VALUE == pipe) return SBOX_TEST_DENIED; + // The second parameter allows us to enforce a whitelist for where the + // pipe should be in the object namespace after creation. + if (argc == 2) { + base::string16 handle_name; + if (GetHandleName(pipe, &handle_name)) { + if (handle_name.compare(0, wcslen(argv[1]), argv[1]) != 0) + return SBOX_TEST_FAILED; + } else { + return SBOX_TEST_FAILED; + } + } + OVERLAPPED overlapped = {0}; overlapped.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL); BOOL result = ::ConnectNamedPipe(pipe, &overlapped); @@ -45,19 +59,59 @@ SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) { return SBOX_TEST_SUCCEEDED; } -// Tests if we can create a pipe in the sandbox. On XP, the sandbox can create -// a pipe without any help but it fails on Vista, this is why we do not test -// the "denied" case. +// Tests if we can create a pipe in the sandbox. TEST(NamedPipePolicyTest, CreatePipe) { TestRunner runner; // TODO(nsylvain): This policy is wrong because "*" is a valid char in a // namedpipe name. Here we apply it like a wildcard. http://b/893603 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, TargetPolicy::NAMEDPIPES_ALLOW_ANY, - L"\\\\.\\pipe\\test*")); + L"\\\\.\\pipe\\test*")); EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); + + // On XP, the sandbox can create a pipe without any help but it fails on + // Vista+, this is why we do not test the "denied" case. + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) { + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh")); + } +} + +// Tests if we can create a pipe with a path traversal in the sandbox. +TEST(NamedPipePolicyTest, CreatePipeTraversal) { + TestRunner runner; + // TODO(nsylvain): This policy is wrong because "*" is a valid char in a + // namedpipe name. Here we apply it like a wildcard. http://b/893603 + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, + TargetPolicy::NAMEDPIPES_ALLOW_ANY, + L"\\\\.\\pipe\\test*")); + + // On XP, the sandbox can create a pipe without any help but it fails on + // Vista+, this is why we do not test the "denied" case. + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) { + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\..\\bleh")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/../bleh")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\../bleh")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/..\\bleh")); + } +} + +// This tests that path canonicalization is actually disabled if we use \\?\ +// syntax. +TEST(NamedPipePolicyTest, CreatePipeCanonicalization) { + // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to + // disable all string parsing and to send the string that follows it straight + // to the file system." + // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx + wchar_t* argv[2] = { L"\\\\?\\pipe\\test\\..\\bleh", + L"\\Device\\NamedPipe\\test" }; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, NamedPipe_Create(2, argv)); } // The same test as CreatePipe but this time using strict interceptions. @@ -73,6 +127,13 @@ TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) { EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); + + // On XP, the sandbox can create a pipe without any help but it fails on + // Vista+, this is why we do not test the "denied" case. + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) { + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh")); + } } } // namespace sandbox diff --git a/chromium/sandbox/win/src/nt_internals.h b/chromium/sandbox/win/src/nt_internals.h index c9aaf927328..e0c74ac1fb4 100644 --- a/chromium/sandbox/win/src/nt_internals.h +++ b/chromium/sandbox/win/src/nt_internals.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright 2013 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. @@ -601,6 +601,11 @@ typedef size_t (__cdecl *strlenFunction)( typedef size_t (__cdecl *wcslenFunction)( IN const wchar_t* _Str); +typedef void* (__cdecl *memcpyFunction)( + IN void* dest, + IN const void* src, + IN size_t count); + typedef NTSTATUS (WINAPI *RtlAnsiStringToUnicodeStringFunction)( IN OUT PUNICODE_STRING DestinationString, IN PANSI_STRING SourceString, @@ -615,5 +620,31 @@ typedef VOID (WINAPI *RtlInitUnicodeStringFunction) ( IN OUT PUNICODE_STRING DestinationString, IN PCWSTR SourceString); +typedef enum _EVENT_TYPE { + NotificationEvent, + SynchronizationEvent +} EVENT_TYPE, *PEVENT_TYPE; + +typedef NTSTATUS (WINAPI* NtOpenDirectoryObjectFunction) ( + PHANDLE DirectoryHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NtQuerySymbolicLinkObjectFunction) ( + HANDLE LinkHandle, + PUNICODE_STRING LinkTarget, + PULONG ReturnedLength); + +typedef NTSTATUS (WINAPI* NtOpenSymbolicLinkObjectFunction) ( + PHANDLE LinkHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes); + +#define DIRECTORY_QUERY 0x0001 +#define DIRECTORY_TRAVERSE 0x0002 +#define DIRECTORY_CREATE_OBJECT 0x0004 +#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008 +#define DIRECTORY_ALL_ACCESS 0x000F + #endif // SANDBOX_WIN_SRC_NT_INTERNALS_H__ diff --git a/chromium/sandbox/win/src/policy_broker.cc b/chromium/sandbox/win/src/policy_broker.cc index fbe46190770..dc5e18c28b9 100644 --- a/chromium/sandbox/win/src/policy_broker.cc +++ b/chromium/sandbox/win/src/policy_broker.cc @@ -73,6 +73,7 @@ bool SetupNtdllImports(TargetProcess *child) { INIT_GLOBAL_RTL(_strnicmp); INIT_GLOBAL_RTL(strlen); INIT_GLOBAL_RTL(wcslen); + INIT_GLOBAL_RTL(memcpy); #ifndef NDEBUG // Verify that the structure is fully initialized. diff --git a/chromium/sandbox/win/src/policy_low_level.cc b/chromium/sandbox/win/src/policy_low_level.cc index 686caa197b0..b6331b9d26e 100644 --- a/chromium/sandbox/win/src/policy_low_level.cc +++ b/chromium/sandbox/win/src/policy_low_level.cc @@ -151,7 +151,7 @@ PolicyRule::PolicyRule(const PolicyRule& other) { bool PolicyRule::GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, uint16 parameter, int state, bool last_call, - int* skip_count, std::wstring* fragment) { + int* skip_count, base::string16* fragment) { // The last opcode must: // 1) Always clear the context. @@ -226,7 +226,7 @@ bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, uint32 last_char = kLastCharIsNone; int state = PENDING_NONE; int skip_count = 0; // counts how many '?' we have seen in a row. - std::wstring fragment; // accumulates the non-wildcard part of the string. + base::string16 fragment; // accumulates the non-wildcard part. while (L'\0' != *current_char) { switch (*current_char) { diff --git a/chromium/sandbox/win/src/policy_low_level.h b/chromium/sandbox/win/src/policy_low_level.h index 025a133ec6a..ca8b36fd409 100644 --- a/chromium/sandbox/win/src/policy_low_level.h +++ b/chromium/sandbox/win/src/policy_low_level.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/policy_engine_params.h" #include "sandbox/win/src/policy_engine_opcodes.h" @@ -163,7 +164,7 @@ class PolicyRule { // in AddStringMatch. bool GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, uint16 parameter, int state, bool last_call, - int* skip_count, std::wstring* fragment); + int* skip_count, base::string16* fragment); // Loop over all generated opcodes and copy them to increasing memory // addresses from opcode_start and copy the extra data (strings usually) into diff --git a/chromium/sandbox/win/src/policy_target_test.cc b/chromium/sandbox/win/src/policy_target_test.cc index ee28260b084..dba670a9989 100644 --- a/chromium/sandbox/win/src/policy_target_test.cc +++ b/chromium/sandbox/win/src/policy_target_test.cc @@ -150,10 +150,11 @@ SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t **argv) { // Use default values to create a new process. STARTUPINFO startup_info = {0}; startup_info.cb = sizeof(startup_info); - base::win::ScopedProcessInformation process_info; + PROCESS_INFORMATION temp_process_info = {}; if (!::CreateProcessW(L"foo.exe", L"foo.exe", NULL, NULL, FALSE, 0, - NULL, NULL, &startup_info, process_info.Receive())) + NULL, NULL, &startup_info, &temp_process_info)) return SBOX_TEST_SUCCEEDED; + base::win::ScopedProcessInformation process_info(temp_process_info); return SBOX_TEST_FAILED; } @@ -228,7 +229,7 @@ TEST(PolicyTargetTest, DesktopPolicy) { wchar_t prog_name[MAX_PATH]; GetModuleFileNameW(NULL, prog_name, MAX_PATH); - std::wstring arguments(L"\""); + base::string16 arguments(L"\""); arguments += prog_name; arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. @@ -239,11 +240,14 @@ TEST(PolicyTargetTest, DesktopPolicy) { TargetPolicy* policy = broker->CreatePolicy(); policy->SetAlternateDesktop(false); policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); + PROCESS_INFORMATION temp_process_info = {}; result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, - target.Receive()); + &temp_process_info); policy->Release(); EXPECT_EQ(SBOX_ALL_OK, result); + if (result == SBOX_ALL_OK) + target.Set(temp_process_info); EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); @@ -252,7 +256,7 @@ TEST(PolicyTargetTest, DesktopPolicy) { EXPECT_NE(::GetThreadDesktop(target.thread_id()), ::GetThreadDesktop(::GetCurrentThreadId())); - std::wstring desktop_name = policy->GetAlternateDesktop(); + base::string16 desktop_name = policy->GetAlternateDesktop(); HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); EXPECT_TRUE(NULL != desk); EXPECT_TRUE(::CloseDesktop(desk)); @@ -288,7 +292,7 @@ TEST(PolicyTargetTest, WinstaPolicy) { wchar_t prog_name[MAX_PATH]; GetModuleFileNameW(NULL, prog_name, MAX_PATH); - std::wstring arguments(L"\""); + base::string16 arguments(L"\""); arguments += prog_name; arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. @@ -299,11 +303,14 @@ TEST(PolicyTargetTest, WinstaPolicy) { TargetPolicy* policy = broker->CreatePolicy(); policy->SetAlternateDesktop(true); policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); + PROCESS_INFORMATION temp_process_info = {}; result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, - target.Receive()); + &temp_process_info); policy->Release(); EXPECT_EQ(SBOX_ALL_OK, result); + if (result == SBOX_ALL_OK) + target.Set(temp_process_info); EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); @@ -312,11 +319,11 @@ TEST(PolicyTargetTest, WinstaPolicy) { EXPECT_NE(::GetThreadDesktop(target.thread_id()), ::GetThreadDesktop(::GetCurrentThreadId())); - std::wstring desktop_name = policy->GetAlternateDesktop(); + base::string16 desktop_name = policy->GetAlternateDesktop(); ASSERT_FALSE(desktop_name.empty()); // Make sure there is a backslash, for the window station name. - EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos); + EXPECT_NE(desktop_name.find_first_of(L'\\'), base::string16::npos); // Isolate the desktop name. desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1); diff --git a/chromium/sandbox/win/src/process_policy_test.cc b/chromium/sandbox/win/src/process_policy_test.cc index babe3214b0c..a03e0bee5e0 100644 --- a/chromium/sandbox/win/src/process_policy_test.cc +++ b/chromium/sandbox/win/src/process_policy_test.cc @@ -50,8 +50,12 @@ sandbox::SboxTestResult CreateProcessHelper(const string16& exe, // Create the process with the unicode version of the API. sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED; - if (!::CreateProcessW(exe_name, const_cast(cmd_line), NULL, NULL, - FALSE, 0, NULL, NULL, &si, pi.Receive())) { + PROCESS_INFORMATION temp_process_info = {}; + if (::CreateProcessW(exe_name, const_cast(cmd_line), NULL, NULL, + FALSE, 0, NULL, NULL, &si, &temp_process_info)) { + pi.Set(temp_process_info); + ret1 = sandbox::SBOX_TEST_SUCCEEDED; + } else { DWORD last_error = GetLastError(); if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || (ERROR_ACCESS_DENIED == last_error) || @@ -60,8 +64,6 @@ sandbox::SboxTestResult CreateProcessHelper(const string16& exe, } else { ret1 = sandbox::SBOX_TEST_FAILED; } - } else { - ret1 = sandbox::SBOX_TEST_SUCCEEDED; } pi.Close(); @@ -73,10 +75,13 @@ sandbox::SboxTestResult CreateProcessHelper(const string16& exe, std::string narrow_cmd_line; if (cmd_line) narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8); - if (!::CreateProcessA( + if (::CreateProcessA( exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL, cmd_line ? const_cast(narrow_cmd_line.c_str()) : NULL, - NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) { + NULL, NULL, FALSE, 0, NULL, NULL, &sia, &temp_process_info)) { + pi.Set(temp_process_info); + ret2 = sandbox::SBOX_TEST_SUCCEEDED; + } else { DWORD last_error = GetLastError(); if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || (ERROR_ACCESS_DENIED == last_error) || @@ -85,8 +90,6 @@ sandbox::SboxTestResult CreateProcessHelper(const string16& exe, } else { ret2 = sandbox::SBOX_TEST_FAILED; } - } else { - ret2 = sandbox::SBOX_TEST_SUCCEEDED; } if (ret1 == ret2) @@ -215,13 +218,14 @@ SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) { string16 path = MakeFullPathToSystem32(argv[0]); - base::win::ScopedProcessInformation pi; STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION temp_process_info = {}; if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, - NULL, NULL, &si, pi.Receive())) { + NULL, NULL, &si, &temp_process_info)) { return SBOX_TEST_FAILED; } + base::win::ScopedProcessInformation pi(temp_process_info); HANDLE token = NULL; BOOL result = diff --git a/chromium/sandbox/win/src/process_thread_dispatcher.cc b/chromium/sandbox/win/src/process_thread_dispatcher.cc index b2331b7f525..39b41328289 100644 --- a/chromium/sandbox/win/src/process_thread_dispatcher.cc +++ b/chromium/sandbox/win/src/process_thread_dispatcher.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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. @@ -29,20 +29,20 @@ namespace { // "c:\program files\test param" will first try to launch c:\program.exe then // c:\program files\test.exe. We don't do that, we stop after at the first // space when there is no quotes. -std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { - std::wstring exe_name; +base::string16 GetPathFromCmdLine(const base::string16 &cmd_line) { + base::string16 exe_name; // Check if it starts with '"'. if (cmd_line[0] == L'\"') { // Find the position of the second '"', this terminates the path. - std::wstring::size_type pos = cmd_line.find(L'\"', 1); - if (std::wstring::npos == pos) + base::string16::size_type pos = cmd_line.find(L'\"', 1); + if (base::string16::npos == pos) return cmd_line; exe_name = cmd_line.substr(1, pos - 1); } else { // There is no '"', that means that the appname is terminated at the // first space. - std::wstring::size_type pos = cmd_line.find(L' '); - if (std::wstring::npos == pos) { + base::string16::size_type pos = cmd_line.find(L' '); + if (base::string16::npos == pos) { // There is no space, the cmd_line contains only the app_name exe_name = cmd_line; } else { @@ -55,7 +55,7 @@ std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { // Returns true is the path in parameter is relative. False if it's // absolute. -bool IsPathRelative(const std::wstring &path) { +bool IsPathRelative(const base::string16 &path) { // A path is Relative if it's not a UNC path beginnning with \\ or a // path beginning with a drive. (i.e. X:\) if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1) @@ -64,8 +64,8 @@ bool IsPathRelative(const std::wstring &path) { } // Converts a relative path to an absolute path. -bool ConvertToAbsolutePath(const std::wstring& child_current_directory, - bool use_env_path, std::wstring *path) { +bool ConvertToAbsolutePath(const base::string16& child_current_directory, + bool use_env_path, base::string16 *path) { wchar_t file_buffer[MAX_PATH]; wchar_t *file_part = NULL; @@ -145,7 +145,7 @@ bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, return false; case IPC_CREATEPROCESSW_TAG: - return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW, + return INTERCEPT_EAT(manager, kKerneldllName, CreateProcessW, CREATE_PROCESSW_ID, 44) && INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA, CREATE_PROCESSA_ID, 44); @@ -201,15 +201,15 @@ bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, return true; } -bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name, - std::wstring* cmd_line, - std::wstring* cur_dir, +bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, base::string16* name, + base::string16* cmd_line, + base::string16* cur_dir, CountedBuffer* info) { if (sizeof(PROCESS_INFORMATION) != info->Size()) return false; // Check if there is an application name. - std::wstring exe_name; + base::string16 exe_name; if (!name->empty()) exe_name = *name; else diff --git a/chromium/sandbox/win/src/process_thread_dispatcher.h b/chromium/sandbox/win/src/process_thread_dispatcher.h index 1cc5743998f..fba27541b32 100644 --- a/chromium/sandbox/win/src/process_thread_dispatcher.h +++ b/chromium/sandbox/win/src/process_thread_dispatcher.h @@ -6,6 +6,7 @@ #define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -35,8 +36,11 @@ class ThreadProcessDispatcher : public Dispatcher { DWORD attributes); // Processes IPC requests coming from calls to CreateProcessW() in the target. - bool CreateProcessW(IPCInfo* ipc, std::wstring* name, std::wstring* cmd_line, - std::wstring* cur_dir, CountedBuffer* info); + bool CreateProcessW(IPCInfo* ipc, + base::string16* name, + base::string16* cmd_line, + base::string16* cur_dir, + CountedBuffer* info); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher); diff --git a/chromium/sandbox/win/src/process_thread_interception.cc b/chromium/sandbox/win/src/process_thread_interception.cc index cb1017b07b6..d351ee546c8 100644 --- a/chromium/sandbox/win/src/process_thread_interception.cc +++ b/chromium/sandbox/win/src/process_thread_interception.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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. @@ -273,12 +273,13 @@ BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, process_information)) { return TRUE; } - DWORD original_error = ::GetLastError(); // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return FALSE; + DWORD original_error = ::GetLastError(); + do { if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), WRITE)) @@ -331,12 +332,13 @@ BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, process_information)) { return TRUE; } - DWORD original_error = ::GetLastError(); // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return FALSE; + DWORD original_error = ::GetLastError(); + do { if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), WRITE)) diff --git a/chromium/sandbox/win/src/process_thread_policy.cc b/chromium/sandbox/win/src/process_thread_policy.cc index 9493b9ea199..85a2f978a93 100644 --- a/chromium/sandbox/win/src/process_thread_policy.cc +++ b/chromium/sandbox/win/src/process_thread_policy.cc @@ -217,8 +217,8 @@ NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &app_name, - const std::wstring &command_line, + const base::string16 &app_name, + const base::string16 &command_line, PROCESS_INFORMATION* process_info) { // The only action supported is ASK_BROKER which means create the process. if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) { diff --git a/chromium/sandbox/win/src/process_thread_policy.h b/chromium/sandbox/win/src/process_thread_policy.h index c35c52b5e6d..2871dcaa276 100644 --- a/chromium/sandbox/win/src/process_thread_policy.h +++ b/chromium/sandbox/win/src/process_thread_policy.h @@ -10,6 +10,7 @@ #include "sandbox/win/src/policy_low_level.h" #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy.h" @@ -71,8 +72,8 @@ class ProcessPolicy { // 'command_line' : The command line passed to the created process. static DWORD CreateProcessWAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &app_name, - const std::wstring &command_line, + const base::string16 &app_name, + const base::string16 &command_line, PROCESS_INFORMATION* process_info); }; diff --git a/chromium/sandbox/win/src/registry_dispatcher.cc b/chromium/sandbox/win/src/registry_dispatcher.cc index f4dc5f5288b..2a92497c7c9 100644 --- a/chromium/sandbox/win/src/registry_dispatcher.cc +++ b/chromium/sandbox/win/src/registry_dispatcher.cc @@ -20,8 +20,8 @@ namespace { // Builds a path using the root directory and the name. -bool GetCompletePath(HANDLE root, const std::wstring& name, - std::wstring* complete_name) { +bool GetCompletePath(HANDLE root, const base::string16& name, + base::string16* complete_name) { if (root) { if (!sandbox::GetPathFromHandle(root, complete_name)) return false; @@ -72,10 +72,10 @@ bool RegistryDispatcher::SetupService(InterceptionManager* manager, } bool RegistryDispatcher::NtCreateKey( - IPCInfo* ipc, std::wstring* name, DWORD attributes, HANDLE root, + IPCInfo* ipc, base::string16* name, DWORD attributes, HANDLE root, DWORD desired_access, DWORD title_index, DWORD create_options) { base::win::ScopedHandle root_handle; - std::wstring real_path = *name; + base::string16 real_path = *name; // If there is a root directory, we need to duplicate the handle to make // it valid in this process. @@ -117,11 +117,11 @@ bool RegistryDispatcher::NtCreateKey( return true; } -bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, std::wstring* name, +bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, base::string16* name, DWORD attributes, HANDLE root, DWORD desired_access) { base::win::ScopedHandle root_handle; - std::wstring real_path = *name; + base::string16 real_path = *name; // If there is a root directory, we need to duplicate the handle to make // it valid in this process. diff --git a/chromium/sandbox/win/src/registry_dispatcher.h b/chromium/sandbox/win/src/registry_dispatcher.h index 782a070e4e2..39f5f54246d 100644 --- a/chromium/sandbox/win/src/registry_dispatcher.h +++ b/chromium/sandbox/win/src/registry_dispatcher.h @@ -6,6 +6,7 @@ #define SANDBOX_SRC_REGISTRY_DISPATCHER_H_ #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -22,12 +23,12 @@ class RegistryDispatcher : public Dispatcher { private: // Processes IPC requests coming from calls to NtCreateKey in the target. - bool NtCreateKey(IPCInfo* ipc, std::wstring* name, DWORD attributes, + bool NtCreateKey(IPCInfo* ipc, base::string16* name, DWORD attributes, HANDLE root, DWORD desired_access, DWORD title_index, DWORD create_options); // Processes IPC requests coming from calls to NtOpenKey in the target. - bool NtOpenKey(IPCInfo* ipc, std::wstring* name, DWORD attributes, + bool NtOpenKey(IPCInfo* ipc, base::string16* name, DWORD attributes, HANDLE root, DWORD desired_access); PolicyBase* policy_base_; diff --git a/chromium/sandbox/win/src/registry_policy.cc b/chromium/sandbox/win/src/registry_policy.cc index 37e6ddbebd9..632525a0c62 100644 --- a/chromium/sandbox/win/src/registry_policy.cc +++ b/chromium/sandbox/win/src/registry_policy.cc @@ -117,7 +117,7 @@ namespace sandbox { bool RegistryPolicy::GenerateRules(const wchar_t* name, TargetPolicy::Semantics semantics, LowLevelPolicy* policy) { - std::wstring resovled_name(name); + base::string16 resovled_name(name); if (resovled_name.empty()) { return false; } @@ -166,7 +166,7 @@ bool RegistryPolicy::GenerateRules(const wchar_t* name, bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &key, + const base::string16 &key, uint32 attributes, HANDLE root_directory, uint32 desired_access, @@ -200,7 +200,7 @@ bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &key, + const base::string16 &key, uint32 attributes, HANDLE root_directory, uint32 desired_access, diff --git a/chromium/sandbox/win/src/registry_policy.h b/chromium/sandbox/win/src/registry_policy.h index 8badde227d5..69af8415d24 100644 --- a/chromium/sandbox/win/src/registry_policy.h +++ b/chromium/sandbox/win/src/registry_policy.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/policy_low_level.h" @@ -30,7 +31,7 @@ class RegistryPolicy { // API that is compatible with the IPC-received parameters. static bool CreateKeyAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &key, + const base::string16 &key, uint32 attributes, HANDLE root_directory, uint32 desired_access, @@ -44,7 +45,7 @@ class RegistryPolicy { // API that is compatible with the IPC-received parameters. static bool OpenKeyAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &key, + const base::string16 &key, uint32 attributes, HANDLE root_directory, uint32 desired_access, diff --git a/chromium/sandbox/win/src/restricted_token.cc b/chromium/sandbox/win/src/restricted_token.cc index 6948d8ada40..64973e98f32 100644 --- a/chromium/sandbox/win/src/restricted_token.cc +++ b/chromium/sandbox/win/src/restricted_token.cc @@ -277,7 +277,7 @@ unsigned RestrictedToken::AddUserSidForDenyOnly() { } unsigned RestrictedToken::DeleteAllPrivileges( - const std::vector *exceptions) { + const std::vector *exceptions) { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; diff --git a/chromium/sandbox/win/src/restricted_token.h b/chromium/sandbox/win/src/restricted_token.h index 4327856f364..6d8e5506918 100644 --- a/chromium/sandbox/win/src/restricted_token.h +++ b/chromium/sandbox/win/src/restricted_token.h @@ -9,6 +9,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/restricted_token_utils.h" #include "sandbox/win/src/security_level.h" #include "sandbox/win/src/sid.h" @@ -119,11 +120,11 @@ class RestrictedToken { // the error. // // Sample usage: - // std::vector privilege_exceptions; + // std::vector privilege_exceptions; // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); // restricted_token.DeleteAllPrivileges(&privilege_exceptions); unsigned DeleteAllPrivileges( - const std::vector *exceptions); + const std::vector *exceptions); // Adds a privilege to the list of privileges to remove in the restricted // token. diff --git a/chromium/sandbox/win/src/restricted_token_unittest.cc b/chromium/sandbox/win/src/restricted_token_unittest.cc index 4948ad1c82f..480106e8f35 100644 --- a/chromium/sandbox/win/src/restricted_token_unittest.cc +++ b/chromium/sandbox/win/src/restricted_token_unittest.cc @@ -353,7 +353,7 @@ TEST(RestrictedTokenTest, DeleteAllPrivilegesException) { RestrictedToken token; HANDLE token_handle = NULL; - std::vector exceptions; + std::vector exceptions; exceptions.push_back(SE_CHANGE_NOTIFY_NAME); ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); diff --git a/chromium/sandbox/win/src/restricted_token_utils.cc b/chromium/sandbox/win/src/restricted_token_utils.cc index 5f1a246689c..f3b18591e8f 100644 --- a/chromium/sandbox/win/src/restricted_token_utils.cc +++ b/chromium/sandbox/win/src/restricted_token_utils.cc @@ -29,7 +29,7 @@ DWORD CreateRestrictedToken(HANDLE *token_handle, RestrictedToken restricted_token; restricted_token.Init(NULL); // Initialized with the current process token - std::vector privilege_exceptions; + std::vector privilege_exceptions; std::vector sid_exceptions; bool deny_sids = true; @@ -183,7 +183,7 @@ DWORD StartRestrictedProcessInJob(wchar_t *command_line, // Start the process STARTUPINFO startup_info = {0}; - base::win::ScopedProcessInformation process_info; + PROCESS_INFORMATION temp_process_info = {}; DWORD flags = CREATE_SUSPENDED; if (base::win::GetVersion() < base::win::VERSION_WIN8) { @@ -202,9 +202,10 @@ DWORD StartRestrictedProcessInJob(wchar_t *command_line, NULL, // Use the environment of the caller. NULL, // Use current directory of the caller. &startup_info, - process_info.Receive())) { + &temp_process_info)) { return ::GetLastError(); } + base::win::ScopedProcessInformation process_info(temp_process_info); // Change the token of the main thread of the new process for the // impersonation token with more rights. @@ -236,7 +237,7 @@ DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, const wchar_t* ace_access, const wchar_t* integrity_level_sid) { // Build the SDDL string for the label. - std::wstring sddl = L"S:("; // SDDL for a SACL. + base::string16 sddl = L"S:("; // SDDL for a SACL. sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label". sddl += L";;"; // No Ace Flags. sddl += ace_access; // Add the ACE access. diff --git a/chromium/sandbox/win/src/sandbox.cc b/chromium/sandbox/win/src/sandbox.cc index 9e4ab08a097..d26daa434e9 100644 --- a/chromium/sandbox/win/src/sandbox.cc +++ b/chromium/sandbox/win/src/sandbox.cc @@ -11,7 +11,7 @@ namespace sandbox { // The section for IPC and policy. -SANDBOX_INTERCEPT HANDLE g_shared_section = NULL; +SANDBOX_INTERCEPT HANDLE g_shared_section; static bool s_is_broker = false; diff --git a/chromium/sandbox/win/src/sandbox_globals.cc b/chromium/sandbox/win/src/sandbox_globals.cc new file mode 100644 index 00000000000..b4ab523921c --- /dev/null +++ b/chromium/sandbox/win/src/sandbox_globals.cc @@ -0,0 +1,18 @@ +// Copyright 2013 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 + +#include "sandbox/win/src/sandbox_nt_types.h" +#include "sandbox/win/src/sandbox_types.h" + +namespace sandbox { + +// The section for IPC and policy. +SANDBOX_INTERCEPT HANDLE g_shared_section = NULL; + +// This is the list of all imported symbols from ntdll.dll. +SANDBOX_INTERCEPT NtExports g_nt = {}; + +} // namespace sandbox diff --git a/chromium/sandbox/win/src/sandbox_nt_types.h b/chromium/sandbox/win/src/sandbox_nt_types.h index 1303ac2627c..46820cfb9f1 100644 --- a/chromium/sandbox/win/src/sandbox_nt_types.h +++ b/chromium/sandbox/win/src/sandbox_nt_types.h @@ -31,6 +31,7 @@ struct NtExports { _strnicmpFunction _strnicmp; strlenFunction strlen; wcslenFunction wcslen; + memcpyFunction memcpy; }; // This is the value used for the ntdll level allocator. diff --git a/chromium/sandbox/win/src/sandbox_nt_util.cc b/chromium/sandbox/win/src/sandbox_nt_util.cc index 71314611283..613d4859dd7 100644 --- a/chromium/sandbox/win/src/sandbox_nt_util.cc +++ b/chromium/sandbox/win/src/sandbox_nt_util.cc @@ -11,7 +11,7 @@ namespace sandbox { // This is the list of all imported symbols from ntdll.dll. -SANDBOX_INTERCEPT NtExports g_nt = { NULL }; +SANDBOX_INTERCEPT NtExports g_nt; } // namespace sandbox @@ -208,15 +208,7 @@ bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { NTSTATUS ret = STATUS_SUCCESS; __try { - if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { - memcpy(destination, source, bytes); - } else { - const char* from = reinterpret_cast(source); - char* to = reinterpret_cast(destination); - for (size_t i = 0; i < bytes; i++) { - to[i] = from[i]; - } - } + g_nt.memcpy(destination, source, bytes); } __except(EXCEPTION_EXECUTE_HANDLER) { ret = GetExceptionCode(); } diff --git a/chromium/sandbox/win/src/sandbox_nt_util.h b/chromium/sandbox/win/src/sandbox_nt_util.h index e5d45fa2f2a..7c543f2ac30 100644 --- a/chromium/sandbox/win/src/sandbox_nt_util.h +++ b/chromium/sandbox/win/src/sandbox_nt_util.h @@ -45,6 +45,8 @@ void __cdecl operator delete(void* memory, void* buffer, #define VERIFY_SUCCESS(action) (action) #endif +#define CHECK_NT(condition) { (condition) ? (void)0 : __debugbreak(); } + #define NOTREACHED_NT() DCHECK_NT(false) namespace sandbox { @@ -94,7 +96,6 @@ enum RequiredAccess { // write) bool ValidParameter(void* buffer, size_t size, RequiredAccess intent); - // Copies data from a user buffer to our buffer. Returns the operation status. NTSTATUS CopyData(void* destination, const void* source, size_t bytes); diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h index 733356a39b8..a9f12451621 100644 --- a/chromium/sandbox/win/src/sandbox_policy.h +++ b/chromium/sandbox/win/src/sandbox_policy.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/security_level.h" @@ -129,7 +130,7 @@ class TargetPolicy { // Returns the name of the alternate desktop used. If an alternate window // station is specified, the name is prepended by the window station name, // followed by a backslash. - virtual std::wstring GetAlternateDesktop() const = 0; + virtual base::string16 GetAlternateDesktop() const = 0; // Precreates the desktop and window station, if any. virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) = 0; diff --git a/chromium/sandbox/win/src/sandbox_utils.cc b/chromium/sandbox/win/src/sandbox_utils.cc index f4511a47513..8631a7c9117 100644 --- a/chromium/sandbox/win/src/sandbox_utils.cc +++ b/chromium/sandbox/win/src/sandbox_utils.cc @@ -19,7 +19,7 @@ bool IsXPSP2OrLater() { (base::win::OSInfo::GetInstance()->service_pack().major >= 2)); } -void InitObjectAttribs(const std::wstring& name, +void InitObjectAttribs(const base::string16& name, ULONG attributes, HANDLE root, OBJECT_ATTRIBUTES* obj_attr, @@ -28,11 +28,11 @@ void InitObjectAttribs(const std::wstring& name, if (!RtlInitUnicodeString) { HMODULE ntdll = ::GetModuleHandle(kNtdllName); RtlInitUnicodeString = reinterpret_cast( - GetProcAddress(ntdll, "RtlInitUnicodeString")); + GetProcAddress(ntdll, "RtlInitUnicodeString")); DCHECK(RtlInitUnicodeString); } RtlInitUnicodeString(uni_name, name.c_str()); InitializeObjectAttributes(obj_attr, uni_name, attributes, root, NULL); } -}; // namespace sandbox +} // namespace sandbox diff --git a/chromium/sandbox/win/src/sandbox_utils.h b/chromium/sandbox/win/src/sandbox_utils.h index 78e76fbc733..3043597b185 100644 --- a/chromium/sandbox/win/src/sandbox_utils.h +++ b/chromium/sandbox/win/src/sandbox_utils.h @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_SRC_SANDBOX_UTILS_H__ -#define SANDBOX_SRC_SANDBOX_UTILS_H__ +#ifndef SANDBOX_SRC_SANDBOX_UTILS_H_ +#define SANDBOX_SRC_SANDBOX_UTILS_H_ #include #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/nt_internals.h" namespace sandbox { @@ -16,12 +17,12 @@ namespace sandbox { // Returns true if the current OS is Windows XP SP2 or later. bool IsXPSP2OrLater(); -void InitObjectAttribs(const std::wstring& name, +void InitObjectAttribs(const base::string16& name, ULONG attributes, HANDLE root, OBJECT_ATTRIBUTES* obj_attr, UNICODE_STRING* uni_name); -}; // namespace sandbox +} // namespace sandbox -#endif // SANDBOX_SRC_SANDBOX_UTILS_H__ +#endif // SANDBOX_SRC_SANDBOX_UTILS_H_ diff --git a/chromium/sandbox/win/src/service_resolver.cc b/chromium/sandbox/win/src/service_resolver.cc index bae698c4576..92f21a7c2c9 100644 --- a/chromium/sandbox/win/src/service_resolver.cc +++ b/chromium/sandbox/win/src/service_resolver.cc @@ -1,11 +1,12 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright 2013 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/service_resolver.h" -#include "base/logging.h" #include "base/win/pe_image.h" +#include "sandbox/win/src/internal_types.h" +#include "sandbox/win/src/sandbox_nt_util.h" namespace sandbox { @@ -24,7 +25,6 @@ NTSTATUS ServiceResolverThunk::ResolveInterceptor( NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module, const char* function_name, void** address) { - DCHECK(address); if (NULL == module) return STATUS_UNSUCCESSFUL; @@ -32,11 +32,15 @@ NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module, *address = module_image.GetProcAddress(function_name); if (NULL == *address) { - NOTREACHED(); + NOTREACHED_NT(); return STATUS_UNSUCCESSFUL; } return STATUS_SUCCESS; } +void ServiceResolverThunk::AllowLocalPatches() { + ntdll_base_ = ::GetModuleHandle(kNtdllName); +} + } // namespace sandbox diff --git a/chromium/sandbox/win/src/service_resolver.h b/chromium/sandbox/win/src/service_resolver.h index 3eb9e083810..00896922049 100644 --- a/chromium/sandbox/win/src/service_resolver.h +++ b/chromium/sandbox/win/src/service_resolver.h @@ -43,6 +43,9 @@ class ServiceResolverThunk : public ResolverThunk { // Implementation of Resolver::GetThunkSize. virtual size_t GetThunkSize() const; + // Call this to set up ntdll_base_ which will allow for local patches. + virtual void AllowLocalPatches(); + protected: // The unit test will use this member to allow local patch on a buffer. HMODULE ntdll_base_; diff --git a/chromium/sandbox/win/src/service_resolver_64.cc b/chromium/sandbox/win/src/service_resolver_64.cc index 32de53a43f4..473ddbc7f16 100644 --- a/chromium/sandbox/win/src/service_resolver_64.cc +++ b/chromium/sandbox/win/src/service_resolver_64.cc @@ -4,8 +4,8 @@ #include "sandbox/win/src/service_resolver.h" -#include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/win_utils.h" namespace { @@ -144,14 +144,14 @@ bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, void* remote_thunk) { - ServiceFullThunk* full_local_thunk = reinterpret_cast( - local_thunk); - ServiceFullThunk* full_remote_thunk = reinterpret_cast( - remote_thunk); + ServiceFullThunk* full_local_thunk = + reinterpret_cast(local_thunk); + ServiceFullThunk* full_remote_thunk = + reinterpret_cast(remote_thunk); // Patch the original code. ServiceEntry local_service; - DCHECK_GE(GetInternalThunkSize(), sizeof(local_service)); + DCHECK_NT(GetInternalThunkSize() >= sizeof(local_service)); if (!SetInternalThunk(&local_service, sizeof(local_service), NULL, interceptor_)) return STATUS_UNSUCCESSFUL; @@ -181,12 +181,12 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, } bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { - NOTREACHED(); + NOTREACHED_NT(); return false; } bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { - NOTREACHED(); + NOTREACHED_NT(); return false; } diff --git a/chromium/sandbox/win/src/service_resolver_unittest.cc b/chromium/sandbox/win/src/service_resolver_unittest.cc index 59105cc49de..b01fedfbf09 100644 --- a/chromium/sandbox/win/src/service_resolver_unittest.cc +++ b/chromium/sandbox/win/src/service_resolver_unittest.cc @@ -44,7 +44,7 @@ class ResolverThunkTest : public T { EXPECT_EQ(STATUS_SUCCESS, ret); target_ = fake_target_; - ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); + return ret; }; @@ -108,6 +108,8 @@ NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, scoped_ptr thunk(new char[thunk_size]); size_t used; + resolver->AllowLocalPatches(); + NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, thunk.get(), thunk_size, &used); @@ -224,4 +226,41 @@ TEST(ServiceResolverTest, MultiplePatchedServices) { #endif } +TEST(ServiceResolverTest, LocalPatchesAllowed) { + sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); + + HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); + ASSERT_TRUE(NULL != ntdll_base); + + const char kFunctionName[] = "NtClose"; + + void* target = ::GetProcAddress(ntdll_base, kFunctionName); + ASSERT_TRUE(NULL != target); + + BYTE service[50]; + memcpy(service, target, sizeof(service)); + static_cast(resolver)->set_target(service); + + // Any pointer will do as an interception_entry_point + void* function_entry = resolver; + size_t thunk_size = resolver->GetThunkSize(); + scoped_ptr thunk(new char[thunk_size]); + size_t used; + + NTSTATUS ret = STATUS_UNSUCCESSFUL; + + // First try patching without having allowed local patches. + ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL, + function_entry, thunk.get(), thunk_size, + &used); + EXPECT_FALSE(NT_SUCCESS(ret)); + + // Now allow local patches and check that things work. + resolver->AllowLocalPatches(); + ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL, + function_entry, thunk.get(), thunk_size, + &used); + EXPECT_EQ(STATUS_SUCCESS, ret); +} + } // namespace diff --git a/chromium/sandbox/win/src/sharedmem_ipc_server.cc b/chromium/sandbox/win/src/sharedmem_ipc_server.cc index e2a30c72366..bf8761e8853 100644 --- a/chromium/sandbox/win/src/sharedmem_ipc_server.cc +++ b/chromium/sandbox/win/src/sharedmem_ipc_server.cc @@ -131,7 +131,7 @@ void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) { for (size_t i = 0; i < kMaxIpcParams; i++) { switch (ipc_params->args[i]) { case WCHAR_TYPE: { - delete reinterpret_cast(args[i]); + delete reinterpret_cast(args[i]); args[i] = NULL; break; } @@ -159,7 +159,7 @@ bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params, ipc_params->args[i] = type; switch (type) { case WCHAR_TYPE: { - scoped_ptr data(new std::wstring); + scoped_ptr data(new base::string16); if (!params->GetParameterStr(i, data.get())) { args[i] = 0; ReleaseArgs(ipc_params, args); diff --git a/chromium/sandbox/win/src/sync_dispatcher.cc b/chromium/sandbox/win/src/sync_dispatcher.cc index eb1c1e4740f..d4b36d5fc85 100644 --- a/chromium/sandbox/win/src/sync_dispatcher.cc +++ b/chromium/sandbox/win/src/sync_dispatcher.cc @@ -1,9 +1,10 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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/sync_dispatcher.h" +#include "base/win/windows_version.h" #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/interception.h" #include "sandbox/win/src/interceptors.h" @@ -24,7 +25,7 @@ SyncDispatcher::SyncDispatcher(PolicyBase* policy_base) }; static const IPCCall open_params = { - {IPC_OPENEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE}, + {IPC_OPENEVENT_TAG, WCHAR_TYPE, ULONG_TYPE}, reinterpret_cast(&SyncDispatcher::OpenEvent) }; @@ -34,19 +35,16 @@ SyncDispatcher::SyncDispatcher(PolicyBase* policy_base) bool SyncDispatcher::SetupService(InterceptionManager* manager, int service) { - if (IPC_CREATEEVENT_TAG == service) - return INTERCEPT_EAT(manager, L"kernel32.dll", CreateEventW, - CREATE_EVENT_ID, 20); - - if (IPC_OPENEVENT_TAG == service) - return INTERCEPT_EAT(manager, L"kernel32.dll", OpenEventW, - OPEN_EVENT_ID, 16); - + if (IPC_CREATEEVENT_TAG == service) { + return INTERCEPT_NT(manager, NtCreateEvent, CREATE_EVENT_ID, 24); + } else if (IPC_OPENEVENT_TAG == service) { + return INTERCEPT_NT(manager, NtOpenEvent, OPEN_EVENT_ID, 16); + } return false; } -bool SyncDispatcher::CreateEvent(IPCInfo* ipc, std::wstring* name, - DWORD manual_reset, DWORD initial_state) { +bool SyncDispatcher::CreateEvent(IPCInfo* ipc, base::string16* name, + DWORD event_type, DWORD initial_state) { const wchar_t* event_name = name->c_str(); CountedParameterSet params; params[NameBased::NAME] = ParamPickerMake(event_name); @@ -55,16 +53,16 @@ bool SyncDispatcher::CreateEvent(IPCInfo* ipc, std::wstring* name, params.GetBase()); HANDLE handle = NULL; DWORD ret = SyncPolicy::CreateEventAction(result, *ipc->client_info, *name, - manual_reset, initial_state, + event_type, initial_state, &handle); // Return operation status on the IPC. - ipc->return_info.win32_result = ret; + ipc->return_info.nt_status = ret; ipc->return_info.handle = handle; return true; } -bool SyncDispatcher::OpenEvent(IPCInfo* ipc, std::wstring* name, - DWORD desired_access, DWORD inherit_handle) { +bool SyncDispatcher::OpenEvent(IPCInfo* ipc, base::string16* name, + DWORD desired_access) { const wchar_t* event_name = name->c_str(); CountedParameterSet params; @@ -75,8 +73,7 @@ bool SyncDispatcher::OpenEvent(IPCInfo* ipc, std::wstring* name, params.GetBase()); HANDLE handle = NULL; DWORD ret = SyncPolicy::OpenEventAction(result, *ipc->client_info, *name, - desired_access, inherit_handle, - &handle); + desired_access, &handle); // Return operation status on the IPC. ipc->return_info.win32_result = ret; ipc->return_info.handle = handle; diff --git a/chromium/sandbox/win/src/sync_dispatcher.h b/chromium/sandbox/win/src/sync_dispatcher.h index 13c8b9d31a0..db44ba4dcd0 100644 --- a/chromium/sandbox/win/src/sync_dispatcher.h +++ b/chromium/sandbox/win/src/sync_dispatcher.h @@ -6,6 +6,7 @@ #define SANDBOX_SRC_SYNC_DISPATCHER_H_ #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -22,12 +23,11 @@ class SyncDispatcher : public Dispatcher { private: // Processes IPC requests coming from calls to CreateEvent in the target. - bool CreateEvent(IPCInfo* ipc, std::wstring* name, DWORD manual_reset, + bool CreateEvent(IPCInfo* ipc, base::string16* name, DWORD event_type, DWORD initial_state); // Processes IPC requests coming from calls to OpenEvent in the target. - bool OpenEvent(IPCInfo* ipc, std::wstring* name, DWORD desired_access, - DWORD inherit_handle); + bool OpenEvent(IPCInfo* ipc, base::string16* name, DWORD desired_access); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(SyncDispatcher); diff --git a/chromium/sandbox/win/src/sync_interception.cc b/chromium/sandbox/win/src/sync_interception.cc index d23572389e3..cafbcb0cc5a 100644 --- a/chromium/sandbox/win/src/sync_interception.cc +++ b/chromium/sandbox/win/src/sync_interception.cc @@ -15,93 +15,147 @@ namespace sandbox { -HANDLE WINAPI TargetCreateEventW(CreateEventWFunction orig_CreateEvent, - LPSECURITY_ATTRIBUTES security_attributes, - BOOL manual_reset, BOOL initial_state, - LPCWSTR name) { - // Check if the process can create it first. - HANDLE handle = orig_CreateEvent(security_attributes, manual_reset, - initial_state, name); - DWORD original_error = ::GetLastError(); - if (NULL != handle) - return handle; +ResultCode ProxyCreateEvent(LPCWSTR name, + BOOL initial_state, + EVENT_TYPE event_type, + void* ipc_memory, + CrossCallReturn* answer) { + CountedParameterSet params; + params[NameBased::NAME] = ParamPickerMake(name); + + if (!QueryBroker(IPC_CREATEEVENT_TAG, params.GetBase())) + return SBOX_ERROR_GENERIC; + + SharedMemIPCClient ipc(ipc_memory); + ResultCode code = CrossCall(ipc, IPC_CREATEEVENT_TAG, name, event_type, + initial_state, answer); + return code; +} + +ResultCode ProxyOpenEvent(LPCWSTR name, + ACCESS_MASK desired_access, + void* ipc_memory, + CrossCallReturn* answer) { + CountedParameterSet params; + params[OpenEventParams::NAME] = ParamPickerMake(name); + params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access); + + if (!QueryBroker(IPC_OPENEVENT_TAG, params.GetBase())) + return SBOX_ERROR_GENERIC; + + SharedMemIPCClient ipc(ipc_memory); + ResultCode code = CrossCall(ipc, IPC_OPENEVENT_TAG, name, desired_access, + answer); + + return code; +} + +NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent, + PHANDLE event_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + EVENT_TYPE event_type, + BOOLEAN initial_state) { + NTSTATUS status = orig_CreateEvent(event_handle, desired_access, + object_attributes, event_type, + initial_state); + if (status != STATUS_ACCESS_DENIED || !object_attributes) + return status; // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return NULL; + return status; do { - if (security_attributes) + if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE)) break; void* memory = GetGlobalIPCMemory(); - if (NULL == memory) + if (memory == NULL) break; - CountedParameterSet params; - params[NameBased::NAME] = ParamPickerMake(name); + OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes; + // The RootDirectory points to BaseNamedObjects. We can ignore it. + object_attribs_copy.RootDirectory = NULL; - if (!QueryBroker(IPC_CREATEEVENT_TAG, params.GetBase())) + wchar_t* name = NULL; + uint32 attributes = 0; + NTSTATUS ret = AllocAndCopyName(&object_attribs_copy, &name, &attributes, + NULL); + if (!NT_SUCCESS(ret) || name == NULL) break; - SharedMemIPCClient ipc(memory); CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_CREATEEVENT_TAG, name, manual_reset, - initial_state, &answer); + answer.nt_status = status; + ResultCode code = ProxyCreateEvent(name, initial_state, event_type, memory, + &answer); + operator delete(name, NT_ALLOC); - if (SBOX_ALL_OK != code) + if (code != SBOX_ALL_OK) { + status = answer.nt_status; break; - - ::SetLastError(answer.win32_result); - return answer.handle; + } + __try { + *event_handle = answer.handle; + status = STATUS_SUCCESS; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } } while (false); - ::SetLastError(original_error); - return NULL; + return status; } -// Interception of OpenEventW on the child process. -// It should never be called directly -HANDLE WINAPI TargetOpenEventW(OpenEventWFunction orig_OpenEvent, - ACCESS_MASK desired_access, BOOL inherit_handle, - LPCWSTR name) { - // Check if the process can open it first. - HANDLE handle = orig_OpenEvent(desired_access, inherit_handle, name); - DWORD original_error = ::GetLastError(); - if (NULL != handle) - return handle; +NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent, + PHANDLE event_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes) { + NTSTATUS status = orig_OpenEvent(event_handle, desired_access, + object_attributes); + if (status != STATUS_ACCESS_DENIED || !object_attributes) + return status; // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return NULL; + return status; do { + if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE)) + break; + void* memory = GetGlobalIPCMemory(); - if (NULL == memory) + if (memory == NULL) break; - uint32 inherit_handle_ipc = inherit_handle; - CountedParameterSet params; - params[OpenEventParams::NAME] = ParamPickerMake(name); - params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access); + OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes; + // The RootDirectory points to BaseNamedObjects. We can ignore it. + object_attribs_copy.RootDirectory = NULL; - if (!QueryBroker(IPC_OPENEVENT_TAG, params.GetBase())) + wchar_t* name = NULL; + uint32 attributes = 0; + NTSTATUS ret = AllocAndCopyName(&object_attribs_copy, &name, &attributes, + NULL); + if (!NT_SUCCESS(ret) || name == NULL) break; - SharedMemIPCClient ipc(memory); CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_OPENEVENT_TAG, name, desired_access, - inherit_handle_ipc, &answer); + answer.nt_status = status; + ResultCode code = ProxyOpenEvent(name, desired_access, memory, &answer); + operator delete(name, NT_ALLOC); - if (SBOX_ALL_OK != code) + if (code != SBOX_ALL_OK) { + status = answer.nt_status; break; - - ::SetLastError(answer.win32_result); - return answer.handle; + } + __try { + *event_handle = answer.handle; + status = STATUS_SUCCESS; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } } while (false); - ::SetLastError(original_error); - return NULL; + return status; } } // namespace sandbox diff --git a/chromium/sandbox/win/src/sync_interception.h b/chromium/sandbox/win/src/sync_interception.h index 6c5c46e9478..0f985a8edc8 100644 --- a/chromium/sandbox/win/src/sync_interception.h +++ b/chromium/sandbox/win/src/sync_interception.h @@ -12,27 +12,32 @@ namespace sandbox { extern "C" { -typedef HANDLE (WINAPI *CreateEventWFunction) ( - LPSECURITY_ATTRIBUTES lpEventAttributes, - DWORD dwDesiredAccess, - BOOL bInheritHandle, - LPCWSTR lpName); - -typedef HANDLE (WINAPI *OpenEventWFunction) ( - BOOL bManualReset, - BOOL bInitialState, - LPCWSTR lpName); - -// Interception of CreateEvent on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW( - CreateEventWFunction orig_CreateEvent, - LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, - BOOL initial_state, LPCWSTR name); - -// Interception of OpenEvent on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW( - OpenEventWFunction orig_OpenEvent, ACCESS_MASK desired_access, - BOOL inherit_handle, LPCWSTR name); +typedef NTSTATUS (WINAPI* NtCreateEventFunction) ( + PHANDLE EventHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + EVENT_TYPE EventType, + BOOLEAN InitialState); + +typedef NTSTATUS (WINAPI *NtOpenEventFunction) ( + PHANDLE EventHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes); + +// Interceptors for NtCreateEvent/NtOpenEvent +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateEvent( + NtCreateEventFunction orig_CreateEvent, + PHANDLE event_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + EVENT_TYPE event_type, + BOOLEAN initial_state); + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent( + NtOpenEventFunction orig_OpenEvent, + PHANDLE event_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes); } // extern "C" diff --git a/chromium/sandbox/win/src/sync_policy.cc b/chromium/sandbox/win/src/sync_policy.cc index 87ef0bdd15a..7b18fe7078e 100644 --- a/chromium/sandbox/win/src/sync_policy.cc +++ b/chromium/sandbox/win/src/sync_policy.cc @@ -7,18 +7,133 @@ #include "sandbox/win/src/sync_policy.h" #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/policy_engine_opcodes.h" #include "sandbox/win/src/policy_params.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/sandbox_utils.h" +#include "sandbox/win/src/sync_interception.h" +#include "sandbox/win/src/win_utils.h" namespace sandbox { +// Provides functionality to resolve a symbolic link within the object +// directory passed in. +NTSTATUS ResolveSymbolicLink(const base::string16& directory_name, + const base::string16& name, + base::string16* target) { + NtOpenDirectoryObjectFunction NtOpenDirectoryObject = NULL; + ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject); + + NtQuerySymbolicLinkObjectFunction NtQuerySymbolicLinkObject = NULL; + ResolveNTFunctionPtr("NtQuerySymbolicLinkObject", + &NtQuerySymbolicLinkObject); + + NtOpenSymbolicLinkObjectFunction NtOpenSymbolicLinkObject = NULL; + ResolveNTFunctionPtr("NtOpenSymbolicLinkObject", &NtOpenSymbolicLinkObject); + + NtCloseFunction NtClose = NULL; + ResolveNTFunctionPtr("NtClose", &NtClose); + + OBJECT_ATTRIBUTES symbolic_link_directory_attributes = {}; + UNICODE_STRING symbolic_link_directory_string = {}; + InitObjectAttribs(directory_name, OBJ_CASE_INSENSITIVE, NULL, + &symbolic_link_directory_attributes, + &symbolic_link_directory_string); + + HANDLE symbolic_link_directory = NULL; + NTSTATUS status = NtOpenDirectoryObject(&symbolic_link_directory, + DIRECTORY_QUERY, + &symbolic_link_directory_attributes); + if (status != STATUS_SUCCESS) { + DLOG(ERROR) << "Failed to open symbolic link directory. Error: " + << status; + return status; + } + + OBJECT_ATTRIBUTES symbolic_link_attributes = {}; + UNICODE_STRING name_string = {}; + InitObjectAttribs(name, OBJ_CASE_INSENSITIVE, symbolic_link_directory, + &symbolic_link_attributes, &name_string); + + HANDLE symbolic_link = NULL; + status = NtOpenSymbolicLinkObject(&symbolic_link, GENERIC_READ, + &symbolic_link_attributes); + NtClose(symbolic_link_directory); + if (status != STATUS_SUCCESS) { + DLOG(ERROR) << "Failed to open symbolic link Error: " << status; + return status; + } + + UNICODE_STRING target_path = {}; + unsigned long target_length = 0; + status = NtQuerySymbolicLinkObject(symbolic_link, &target_path, + &target_length); + if (status != STATUS_BUFFER_TOO_SMALL) { + NtClose(symbolic_link); + DLOG(ERROR) << "Failed to get length for symbolic link target. Error: " + << status; + return status; + } + + target_path.Buffer = new wchar_t[target_length + 1]; + target_path.Length = 0; + target_path.MaximumLength = target_length; + status = NtQuerySymbolicLinkObject(symbolic_link, &target_path, + &target_length); + if (status == STATUS_SUCCESS) { + target->assign(target_path.Buffer, target_length); + } else { + DLOG(ERROR) << "Failed to resolve symbolic link. Error: " << status; + } + + NtClose(symbolic_link); + delete[] target_path.Buffer; + return status; +} + +NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) { + static HANDLE base_named_objects_handle = NULL; + if (base_named_objects_handle) { + *directory = base_named_objects_handle; + return STATUS_SUCCESS; + } + + NtOpenDirectoryObjectFunction NtOpenDirectoryObject = NULL; + ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject); + + DWORD session_id = 0; + ProcessIdToSessionId(::GetCurrentProcessId(), &session_id); + + base::string16 base_named_objects_path; + + NTSTATUS status = ResolveSymbolicLink(L"\\Sessions\\BNOLINKS", + base::StringPrintf(L"%d", session_id), + &base_named_objects_path); + if (status != STATUS_SUCCESS) { + DLOG(ERROR) << "Failed to resolve BaseNamedObjects path. Error: " + << status; + return status; + } + + UNICODE_STRING directory_name = {}; + OBJECT_ATTRIBUTES object_attributes = {}; + InitObjectAttribs(base_named_objects_path, OBJ_CASE_INSENSITIVE, NULL, + &object_attributes, &directory_name); + status = NtOpenDirectoryObject(&base_named_objects_handle, + DIRECTORY_ALL_ACCESS, + &object_attributes); + if (status == STATUS_SUCCESS) + *directory = base_named_objects_handle; + return status; +} + bool SyncPolicy::GenerateRules(const wchar_t* name, TargetPolicy::Semantics semantics, LowLevelPolicy* policy) { - std::wstring mod_name(name); + base::string16 mod_name(name); if (mod_name.empty()) { return false; } @@ -63,50 +178,76 @@ bool SyncPolicy::GenerateRules(const wchar_t* name, DWORD SyncPolicy::CreateEventAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &event_name, - uint32 manual_reset, + const base::string16 &event_name, + uint32 event_type, uint32 initial_state, HANDLE *handle) { + NtCreateEventFunction NtCreateEvent = NULL; + ResolveNTFunctionPtr("NtCreateEvent", &NtCreateEvent); + // The only action supported is ASK_BROKER which means create the requested // file as specified. if (ASK_BROKER != eval_result) return false; - HANDLE local_handle = ::CreateEvent(NULL, manual_reset, initial_state, - event_name.c_str()); + HANDLE object_directory = NULL; + NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory); + if (status != STATUS_SUCCESS) + return status; + + UNICODE_STRING unicode_event_name = {}; + OBJECT_ATTRIBUTES object_attributes = {}; + InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory, + &object_attributes, &unicode_event_name); + + HANDLE local_handle = NULL; + status = NtCreateEvent(&local_handle, EVENT_ALL_ACCESS, &object_attributes, + static_cast(event_type), initial_state); if (NULL == local_handle) - return ::GetLastError(); + return status; if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - return ERROR_ACCESS_DENIED; + return STATUS_ACCESS_DENIED; } - return ERROR_SUCCESS; + return status; } DWORD SyncPolicy::OpenEventAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &event_name, + const base::string16 &event_name, uint32 desired_access, - uint32 inherit_handle, HANDLE *handle) { + NtOpenEventFunction NtOpenEvent = NULL; + ResolveNTFunctionPtr("NtOpenEvent", &NtOpenEvent); + // The only action supported is ASK_BROKER which means create the requested - // file as specified. + // event as specified. if (ASK_BROKER != eval_result) return false; - HANDLE local_handle = ::OpenEvent(desired_access, FALSE, - event_name.c_str()); + HANDLE object_directory = NULL; + NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory); + if (status != STATUS_SUCCESS) + return status; + + UNICODE_STRING unicode_event_name = {}; + OBJECT_ATTRIBUTES object_attributes = {}; + InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory, + &object_attributes, &unicode_event_name); + + HANDLE local_handle = NULL; + status = NtOpenEvent(&local_handle, desired_access, &object_attributes); if (NULL == local_handle) - return ::GetLastError(); + return status; if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, inherit_handle, + client_info.process, handle, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - return ERROR_ACCESS_DENIED; + return STATUS_ACCESS_DENIED; } - return ERROR_SUCCESS; + return status; } } // namespace sandbox diff --git a/chromium/sandbox/win/src/sync_policy.h b/chromium/sandbox/win/src/sync_policy.h index 2b8b4227025..4383998205f 100644 --- a/chromium/sandbox/win/src/sync_policy.h +++ b/chromium/sandbox/win/src/sync_policy.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/policy_low_level.h" @@ -34,15 +35,14 @@ class SyncPolicy { // eval_result is the desired policy action to accomplish. static DWORD CreateEventAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &event_name, - uint32 manual_reset, + const base::string16 &event_name, + uint32 event_type, uint32 initial_state, HANDLE *handle); static DWORD OpenEventAction(EvalResult eval_result, const ClientInfo& client_info, - const std::wstring &event_name, + const base::string16 &event_name, uint32 desired_access, - uint32 inherit_handle, HANDLE *handle); }; diff --git a/chromium/sandbox/win/src/sync_policy_test.cc b/chromium/sandbox/win/src/sync_policy_test.cc index 87d03f1028f..ced5498f53a 100644 --- a/chromium/sandbox/win/src/sync_policy_test.cc +++ b/chromium/sandbox/win/src/sync_policy_test.cc @@ -87,7 +87,7 @@ SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t **argv) { } // Tests the creation of events using all the possible combinations. -TEST(SyncPolicyTest, TestEvent) { +TEST(SyncPolicyTest, DISABLED_TestEvent) { TestRunner runner; EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, TargetPolicy::EVENTS_ALLOW_ANY, @@ -111,7 +111,7 @@ TEST(SyncPolicyTest, TestEvent) { } // Tests opening events with read only access. -TEST(SyncPolicyTest, TestEventReadOnly) { +TEST(SyncPolicyTest, DISABLED_TestEventReadOnly) { TestRunner runner; EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, TargetPolicy::EVENTS_ALLOW_READONLY, diff --git a/chromium/sandbox/win/src/target_process.cc b/chromium/sandbox/win/src/target_process.cc index 9300ccee076..a2d630c9452 100644 --- a/chromium/sandbox/win/src/target_process.cc +++ b/chromium/sandbox/win/src/target_process.cc @@ -131,8 +131,7 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, flags |= CREATE_BREAKAWAY_FROM_JOB; } - base::win::ScopedProcessInformation process_info; - + PROCESS_INFORMATION temp_process_info = {}; if (!::CreateProcessAsUserW(lockdown_token_, exe_path, cmd_line.get(), @@ -143,9 +142,10 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, NULL, // Use the environment of the caller. NULL, // Use current directory of the caller. startup_info.startup_info(), - process_info.Receive())) { + &temp_process_info)) { return ::GetLastError(); } + base::win::ScopedProcessInformation process_info(temp_process_info); lockdown_token_.Close(); DWORD win_result = ERROR_SUCCESS; diff --git a/chromium/sandbox/win/src/win_utils.cc b/chromium/sandbox/win/src/win_utils.cc index f5c479a35a2..53a12a4f292 100644 --- a/chromium/sandbox/win/src/win_utils.cc +++ b/chromium/sandbox/win/src/win_utils.cc @@ -6,10 +6,10 @@ #include -#include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "sandbox/win/src/internal_types.h" #include "sandbox/win/src/nt_internals.h" +#include "sandbox/win/src/sandbox_nt_util.h" namespace { @@ -33,7 +33,7 @@ const KnownReservedKey kKnownKey[] = { }; // Returns true if the provided path points to a pipe. -bool IsPipe(const std::wstring& path) { +bool IsPipe(const base::string16& path) { size_t start = 0; if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix)) start = sandbox::kNTPrefixLen; @@ -46,7 +46,7 @@ bool IsPipe(const std::wstring& path) { namespace sandbox { -HKEY GetReservedKeyFromName(const std::wstring& name) { +HKEY GetReservedKeyFromName(const base::string16& name) { for (size_t i = 0; i < arraysize(kKnownKey); ++i) { if (name == kKnownKey[i].name) return kKnownKey[i].key; @@ -55,7 +55,7 @@ HKEY GetReservedKeyFromName(const std::wstring& name) { return NULL; } -bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) { +bool ResolveRegistryName(base::string16 name, base::string16* resolved_name) { for (size_t i = 0; i < arraysize(kKnownKey); ++i) { if (name.find(kKnownKey[i].name) == 0) { HKEY key; @@ -79,8 +79,8 @@ bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) { return false; } -DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { - std::wstring path = full_path; +DWORD IsReparsePoint(const base::string16& full_path, bool* result) { + base::string16 path = full_path; // Remove the nt prefix. if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) @@ -92,7 +92,7 @@ DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { return ERROR_SUCCESS; } - std::wstring::size_type last_pos = std::wstring::npos; + base::string16::size_type last_pos = base::string16::npos; do { path = path.substr(0, last_pos); @@ -104,7 +104,7 @@ DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { error != ERROR_PATH_NOT_FOUND && error != ERROR_INVALID_NAME) { // Unexpected error. - NOTREACHED(); + NOTREACHED_NT(); return error; } } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) { @@ -114,7 +114,7 @@ DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { } last_pos = path.rfind(L'\\'); - } while (last_pos != std::wstring::npos); + } while (last_pos != base::string16::npos); *result = false; return ERROR_SUCCESS; @@ -123,14 +123,14 @@ DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { // We get a |full_path| of the form \??\c:\some\foo\bar, and the name that // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar. bool SameObject(HANDLE handle, const wchar_t* full_path) { - std::wstring path(full_path); - DCHECK(!path.empty()); + base::string16 path(full_path); + DCHECK_NT(!path.empty()); // Check if it's a pipe. if (IsPipe(path)) return true; - std::wstring actual_path; + base::string16 actual_path; if (!GetPathFromHandle(handle, &actual_path)) return false; @@ -145,7 +145,7 @@ bool SameObject(HANDLE handle, const wchar_t* full_path) { // Look for the drive letter. size_t colon_pos = path.find(L':'); - if (colon_pos == 0 || colon_pos == std::wstring::npos) + if (colon_pos == 0 || colon_pos == base::string16::npos) return false; // Only one character for the drive. @@ -180,11 +180,11 @@ bool SameObject(HANDLE handle, const wchar_t* full_path) { return true; } -bool ConvertToLongPath(const std::wstring& short_path, - std::wstring* long_path) { +bool ConvertToLongPath(const base::string16& short_path, + base::string16* long_path) { // Check if the path is a NT path. bool is_nt_path = false; - std::wstring path = short_path; + base::string16 path = short_path; if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) { path = path.substr(kNTPrefixLen); is_nt_path = true; @@ -206,12 +206,12 @@ bool ConvertToLongPath(const std::wstring& short_path, ERROR_PATH_NOT_FOUND == last_error || ERROR_INVALID_NAME == last_error)) { // The file does not exist, but maybe a sub path needs to be expanded. - std::wstring::size_type last_slash = path.rfind(L'\\'); - if (std::wstring::npos == last_slash) + base::string16::size_type last_slash = path.rfind(L'\\'); + if (base::string16::npos == last_slash) return false; - std::wstring begin = path.substr(0, last_slash); - std::wstring end = path.substr(last_slash); + base::string16 begin = path.substr(0, last_slash); + base::string16 end = path.substr(last_slash); if (!ConvertToLongPath(begin, &begin)) return false; @@ -236,7 +236,7 @@ bool ConvertToLongPath(const std::wstring& short_path, return false; } -bool GetPathFromHandle(HANDLE handle, std::wstring* path) { +bool GetPathFromHandle(HANDLE handle, base::string16* path) { NtQueryObjectFunction NtQueryObject = NULL; ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); @@ -265,7 +265,8 @@ bool GetPathFromHandle(HANDLE handle, std::wstring* path) { return true; } -bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) { +bool GetNtPathFromWin32Path(const base::string16& path, + base::string16* nt_path) { HANDLE file = ::CreateFileW(path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); @@ -319,5 +320,5 @@ void ResolveNTFunctionPtr(const char* name, void* ptr) { *function_ptr = ::GetProcAddress(ntdll, name); } - CHECK(*function_ptr); + CHECK_NT(*function_ptr); } diff --git a/chromium/sandbox/win/src/win_utils.h b/chromium/sandbox/win/src/win_utils.h index a80bb81fa24..9b58d1d10fb 100644 --- a/chromium/sandbox/win/src/win_utils.h +++ b/chromium/sandbox/win/src/win_utils.h @@ -7,7 +7,9 @@ #include #include + #include "base/basictypes.h" +#include "base/strings/string16.h" namespace sandbox { @@ -65,35 +67,37 @@ class SingletonBase { // Convert a short path (C:\path~1 or \\??\\c:\path~1) to the long version of // the path. If the path is not a valid filesystem path, the function returns // false and the output parameter is not modified. -bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path); +bool ConvertToLongPath(const base::string16& short_path, + base::string16* long_path); // Sets result to true if the path contains a reparse point. The return value // is ERROR_SUCCESS when the function succeeds or the appropriate error code // when the function fails. // This function is not smart. It looks for each element in the path and // returns true if any of them is a reparse point. -DWORD IsReparsePoint(const std::wstring& full_path, bool* result); +DWORD IsReparsePoint(const base::string16& full_path, bool* result); // Returns true if the handle corresponds to the object pointed by this path. bool SameObject(HANDLE handle, const wchar_t* full_path); // Resolves a handle to an nt path. Returns true if the handle can be resolved. -bool GetPathFromHandle(HANDLE handle, std::wstring* path); +bool GetPathFromHandle(HANDLE handle, base::string16* path); // Resolves a win32 path to an nt path using GetPathFromHandle. The path must // exist. Returs true if the translation was succesful. -bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path); +bool GetNtPathFromWin32Path(const base::string16& path, + base::string16* nt_path); // Translates a reserved key name to its handle. // For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE. // Returns NULL if the name does not represent any reserved key name. -HKEY GetReservedKeyFromName(const std::wstring& name); +HKEY GetReservedKeyFromName(const base::string16& name); // Resolves a user-readable registry path to a system-readable registry path. // For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to // \\registry\\machine\\software\\microsoft. Returns false if the path // cannot be resolved. -bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name); +bool ResolveRegistryName(base::string16 name, base::string16* resolved_name); // Writes |length| bytes from the provided |buffer| into the address space of // |child_process|, at the specified |address|, preserving the original write diff --git a/chromium/sandbox/win/src/win_utils_unittest.cc b/chromium/sandbox/win/src/win_utils_unittest.cc index 72653167eb1..37366544d47 100644 --- a/chromium/sandbox/win/src/win_utils_unittest.cc +++ b/chromium/sandbox/win/src/win_utils_unittest.cc @@ -27,10 +27,10 @@ TEST(WinUtils, IsReparsePoint) { EXPECT_FALSE(result); // We have to fix Bug 32224 to pass this test. - std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar"; + base::string16 not_found = base::string16(my_folder) + L"\\foo\\bar"; // EXPECT_EQ(ERROR_PATH_NOT_FOUND, IsReparsePoint(not_found, &result)); - std::wstring new_file = std::wstring(my_folder) + L"\\foo"; + base::string16 new_file = base::string16(my_folder) + L"\\foo"; EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); EXPECT_FALSE(result); @@ -40,7 +40,7 @@ TEST(WinUtils, IsReparsePoint) { OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); EXPECT_NE(INVALID_HANDLE_VALUE, dir); - std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory; + base::string16 temp_dir_nt = base::string16(L"\\??\\") + temp_directory; EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); @@ -64,16 +64,17 @@ TEST(WinUtils, SameObject) { ASSERT_TRUE(::DeleteFile(my_folder)); ASSERT_TRUE(::CreateDirectory(my_folder, NULL)); - std::wstring folder(my_folder); - std::wstring file_name = folder + L"\\foo.txt"; + base::string16 folder(my_folder); + base::string16 file_name = folder + L"\\foo.txt"; const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; base::win::ScopedHandle file(CreateFile( file_name.c_str(), GENERIC_WRITE, kSharing, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL)); EXPECT_TRUE(file.IsValid()); - std::wstring file_name_nt1 = std::wstring(L"\\??\\") + file_name; - std::wstring file_name_nt2 = std::wstring(L"\\??\\") + folder + L"\\FOO.txT"; + base::string16 file_name_nt1 = base::string16(L"\\??\\") + file_name; + base::string16 file_name_nt2 = + base::string16(L"\\??\\") + folder + L"\\FOO.txT"; EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str())); EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str())); diff --git a/chromium/sandbox/win/src/window.cc b/chromium/sandbox/win/src/window.cc index d8de9672726..6b5766b325b 100644 --- a/chromium/sandbox/win/src/window.cc +++ b/chromium/sandbox/win/src/window.cc @@ -8,6 +8,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "sandbox/win/src/acl.h" +#include "sandbox/win/src/sid.h" namespace { @@ -46,14 +48,15 @@ ResultCode CreateAltWindowStation(HWINSTA* winsta) { *winsta = ::CreateWindowStationW(NULL, 0, WINSTA_ALL_ACCESS, &attributes); LocalFree(attributes.lpSecurityDescriptor); - if (*winsta) + if (*winsta) { return SBOX_ALL_OK; + } return SBOX_ERROR_CANNOT_CREATE_WINSTATION; } ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) { - std::wstring desktop_name = L"sbox_alternate_desktop_"; + base::string16 desktop_name = L"sbox_alternate_desktop_"; // Append the current PID to the desktop name. wchar_t buffer[16]; @@ -94,20 +97,30 @@ ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) { } } - if (*desktop) + if (*desktop) { + // Replace the DACL on the new Desktop with a reduced privilege version. + // We can soft fail on this for now, as it's just an extra mitigation. + static const ACCESS_MASK kDesktopDenyMask = WRITE_DAC | WRITE_OWNER | + DESKTOP_HOOKCONTROL | + DESKTOP_JOURNALPLAYBACK | + DESKTOP_JOURNALRECORD | + DESKTOP_SWITCHDESKTOP; + AddKnownSidToObject(*desktop, SE_WINDOW_OBJECT, Sid(WinRestrictedCodeSid), + DENY_ACCESS, kDesktopDenyMask); return SBOX_ALL_OK; + } return SBOX_ERROR_CANNOT_CREATE_DESKTOP; } -std::wstring GetWindowObjectName(HANDLE handle) { +base::string16 GetWindowObjectName(HANDLE handle) { // Get the size of the name. DWORD size = 0; ::GetUserObjectInformation(handle, UOI_NAME, NULL, 0, &size); if (!size) { NOTREACHED(); - return std::wstring(); + return base::string16(); } // Create the buffer that will hold the name. @@ -117,19 +130,19 @@ std::wstring GetWindowObjectName(HANDLE handle) { if (!::GetUserObjectInformation(handle, UOI_NAME, name_buffer.get(), size, &size)) { NOTREACHED(); - return std::wstring(); + return base::string16(); } - return std::wstring(name_buffer.get()); + return base::string16(name_buffer.get()); } -std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) { +base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop) { if (!desktop) { NOTREACHED(); - return std::wstring(); + return base::string16(); } - std::wstring name; + base::string16 name; if (winsta) { name = GetWindowObjectName(winsta); name += L'\\'; diff --git a/chromium/sandbox/win/src/window.h b/chromium/sandbox/win/src/window.h index e8233e734ee..62fe7c47421 100644 --- a/chromium/sandbox/win/src/window.h +++ b/chromium/sandbox/win/src/window.h @@ -8,6 +8,7 @@ #include #include +#include "base/strings/string16.h" #include "sandbox/win/src/sandbox_types.h" namespace sandbox { @@ -26,13 +27,13 @@ namespace sandbox { ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop); // Returns the name of a desktop or a window station. - std::wstring GetWindowObjectName(HANDLE handle); + base::string16 GetWindowObjectName(HANDLE handle); // Returns the name of the desktop referenced by |desktop|. If a window // station is specified, the name is prepended with the window station name, // followed by a backslash. This name can be used as the lpDesktop parameter // to CreateProcess. - std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop); + base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop); } // namespace sandbox diff --git a/chromium/sandbox/win/wow_helper/wow_helper.cc b/chromium/sandbox/win/wow_helper/wow_helper.cc index 2a27a85e91e..847190ac8b8 100644 --- a/chromium/sandbox/win/wow_helper/wow_helper.cc +++ b/chromium/sandbox/win/wow_helper/wow_helper.cc @@ -27,7 +27,7 @@ inline typename string_type::value_type* WriteInto(string_type* str, } // Grabbed from base/string_util.cc -std::string WideToMultiByte(const std::wstring& wide, UINT code_page) { +std::string WideToMultiByte(const base::string16& wide, UINT code_page) { if (wide.length() == 0) return std::string(); @@ -46,7 +46,7 @@ std::string WideToMultiByte(const std::wstring& wide, UINT code_page) { } // Grabbed from base/string_util.cc -std::string WideToUTF8(const std::wstring& wide) { +std::string WideToUTF8(const base::string16& wide) { return WideToMultiByte(wide, CP_UTF8); } -- cgit v1.2.1