summaryrefslogtreecommitdiff
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/sandbox
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
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 <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/OWNERS2
-rw-r--r--chromium/sandbox/linux/sandbox_linux.gypi67
-rw-r--r--chromium/sandbox/linux/sandbox_linux_test_sources.gypi6
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc175
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h43
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc146
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h39
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc214
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h58
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc981
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h105
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/Makefile30
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/basicblock.cc11
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/basicblock.h22
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/bpf_tests.h72
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen.cc347
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen.h71
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc10
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/demo.cc20
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/die.cc35
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/die.h37
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode.cc50
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode.h61
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc14
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/instruction.h17
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/port.h36
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc789
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h81
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h35
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h23
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc626
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.cc16
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.h101
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc35
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h15
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc7
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc112
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/trap.cc103
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/trap.h47
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/verifier.cc519
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/verifier.h13
-rw-r--r--chromium/sandbox/linux/services/broker_process.cc33
-rw-r--r--chromium/sandbox/linux/services/broker_process.h43
-rw-r--r--chromium/sandbox/linux/services/broker_process_unittest.cc134
-rw-r--r--chromium/sandbox/linux/services/credentials.cc272
-rw-r--r--chromium/sandbox/linux/services/credentials.h79
-rw-r--r--chromium/sandbox/linux/services/credentials_unittest.cc215
-rw-r--r--chromium/sandbox/linux/services/init_process_reaper.cc101
-rw-r--r--chromium/sandbox/linux/services/init_process_reaper.h23
-rw-r--r--chromium/sandbox/linux/services/thread_helpers.cc84
-rw-r--r--chromium/sandbox/linux/services/thread_helpers.h33
-rw-r--r--chromium/sandbox/linux/services/thread_helpers_unittests.cc93
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc6
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client.h6
-rw-r--r--chromium/sandbox/linux/suid/common/sandbox.h3
-rw-r--r--chromium/sandbox/linux/suid/process_util_linux.c31
-rw-r--r--chromium/sandbox/linux/suid/sandbox.c28
-rw-r--r--chromium/sandbox/win/sandbox_poc/main_ui_window.cc21
-rw-r--r--chromium/sandbox/win/sandbox_poc/main_ui_window.h13
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc4
-rw-r--r--chromium/sandbox/win/sandbox_poc/sandbox.cc8
-rw-r--r--chromium/sandbox/win/sandbox_win.gypi2
-rw-r--r--chromium/sandbox/win/src/Wow64.cc11
-rw-r--r--chromium/sandbox/win/src/acl.cc20
-rw-r--r--chromium/sandbox/win/src/acl.h20
-rw-r--r--chromium/sandbox/win/src/crosscall_params.h3
-rw-r--r--chromium/sandbox/win/src/crosscall_server.cc2
-rw-r--r--chromium/sandbox/win/src/crosscall_server.h3
-rw-r--r--chromium/sandbox/win/src/file_policy_test.cc30
-rw-r--r--chromium/sandbox/win/src/filesystem_dispatcher.cc10
-rw-r--r--chromium/sandbox/win/src/filesystem_dispatcher.h13
-rw-r--r--chromium/sandbox/win/src/filesystem_policy.cc12
-rw-r--r--chromium/sandbox/win/src/filesystem_policy.h11
-rw-r--r--chromium/sandbox/win/src/handle_policy_test.cc10
-rw-r--r--chromium/sandbox/win/src/interception.cc9
-rw-r--r--chromium/sandbox/win/src/interception.h11
-rw-r--r--chromium/sandbox/win/src/interceptors_64.cc27
-rw-r--r--chromium/sandbox/win/src/interceptors_64.h17
-rw-r--r--chromium/sandbox/win/src/ipc_unittest.cc4
-rw-r--r--chromium/sandbox/win/src/job_unittest.cc5
-rw-r--r--chromium/sandbox/win/src/named_pipe_dispatcher.cc34
-rw-r--r--chromium/sandbox/win/src/named_pipe_dispatcher.h3
-rw-r--r--chromium/sandbox/win/src/named_pipe_interception.cc4
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy.cc2
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy.h3
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy_test.cc75
-rw-r--r--chromium/sandbox/win/src/nt_internals.h33
-rw-r--r--chromium/sandbox/win/src/policy_broker.cc1
-rw-r--r--chromium/sandbox/win/src/policy_low_level.cc4
-rw-r--r--chromium/sandbox/win/src/policy_low_level.h3
-rw-r--r--chromium/sandbox/win/src/policy_target_test.cc25
-rw-r--r--chromium/sandbox/win/src/process_policy_test.cc24
-rw-r--r--chromium/sandbox/win/src/process_thread_dispatcher.cc30
-rw-r--r--chromium/sandbox/win/src/process_thread_dispatcher.h8
-rw-r--r--chromium/sandbox/win/src/process_thread_interception.cc8
-rw-r--r--chromium/sandbox/win/src/process_thread_policy.cc4
-rw-r--r--chromium/sandbox/win/src/process_thread_policy.h5
-rw-r--r--chromium/sandbox/win/src/registry_dispatcher.cc12
-rw-r--r--chromium/sandbox/win/src/registry_dispatcher.h5
-rw-r--r--chromium/sandbox/win/src/registry_policy.cc6
-rw-r--r--chromium/sandbox/win/src/registry_policy.h5
-rw-r--r--chromium/sandbox/win/src/restricted_token.cc2
-rw-r--r--chromium/sandbox/win/src/restricted_token.h5
-rw-r--r--chromium/sandbox/win/src/restricted_token_unittest.cc2
-rw-r--r--chromium/sandbox/win/src/restricted_token_utils.cc9
-rw-r--r--chromium/sandbox/win/src/sandbox.cc2
-rw-r--r--chromium/sandbox/win/src/sandbox_globals.cc18
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_types.h1
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.cc12
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.h3
-rw-r--r--chromium/sandbox/win/src/sandbox_policy.h3
-rw-r--r--chromium/sandbox/win/src/sandbox_utils.cc6
-rw-r--r--chromium/sandbox/win/src/sandbox_utils.h11
-rw-r--r--chromium/sandbox/win/src/service_resolver.cc12
-rw-r--r--chromium/sandbox/win/src/service_resolver.h3
-rw-r--r--chromium/sandbox/win/src/service_resolver_64.cc16
-rw-r--r--chromium/sandbox/win/src/service_resolver_unittest.cc41
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_server.cc4
-rw-r--r--chromium/sandbox/win/src/sync_dispatcher.cc33
-rw-r--r--chromium/sandbox/win/src/sync_dispatcher.h6
-rw-r--r--chromium/sandbox/win/src/sync_interception.cc156
-rw-r--r--chromium/sandbox/win/src/sync_interception.h47
-rw-r--r--chromium/sandbox/win/src/sync_policy.cc175
-rw-r--r--chromium/sandbox/win/src/sync_policy.h8
-rw-r--r--chromium/sandbox/win/src/sync_policy_test.cc4
-rw-r--r--chromium/sandbox/win/src/target_process.cc6
-rw-r--r--chromium/sandbox/win/src/win_utils.cc47
-rw-r--r--chromium/sandbox/win/src/win_utils.h16
-rw-r--r--chromium/sandbox/win/src/win_utils_unittest.cc15
-rw-r--r--chromium/sandbox/win/src/window.cc33
-rw-r--r--chromium/sandbox/win/src/window.h5
-rw-r--r--chromium/sandbox/win/wow_helper/wow_helper.cc4
132 files changed, 5451 insertions, 2189 deletions
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',
@@ -120,6 +127,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',
'type': 'executable',
@@ -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 <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#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 <unistd.h>
+
+#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<size_t>(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<volatile char*>(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<volatile char*>(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<volatile char*>(clone_flags & 0xFFFFFF);
+ *addr = '\0';
+ }
+ // Hit the NULL page if this fails to fault.
+ addr = reinterpret_cast<volatile char*>(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<volatile char*>(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<volatile char*>(request & 0xFFFF);
+ *addr = '\0';
+ // Hit the NULL page if this fails.
+ addr = reinterpret_cast<volatile char*>(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 <errno.h>
+#include <fcntl.h>
+#include <fcntl.h>
+#include <linux/net.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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 T> class Less {
+ template <class T>
+ 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<Instruction *> instructions;
+ std::vector<Instruction*> 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<aux>::AuxType& BPF_AUX); \
- TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
- sandbox::BpfTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy); \
- sandbox::BpfTests<aux>::RunTestInProcess( \
- sandbox::BpfTests<aux>::TestWrapper, &arg, \
- death); \
- } \
- void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_AUX)
+#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux...) \
+ void BPF_TEST_##test_name(sandbox::BPFTests<aux>::AuxType& BPF_AUX); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::BPFTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy); \
+ sandbox::BPFTests<aux>::RunTestInProcess( \
+ sandbox::BPFTests<aux>::TestWrapper, &arg, death); \
+ } \
+ void BPF_TEST_##test_name(sandbox::BPFTests<aux>::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 Aux = int[0]>
-class BpfTests : public UnitTests {
+template <class Aux = int[0]>
+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<TestArgs *>(void_arg);
- playground2::Die::EnableSimpleExit();
- if (playground2::Sandbox::SupportsSeccompSandbox(-1) ==
- playground2::Sandbox::STATUS_AVAILABLE) {
+ static void TestWrapper(void* void_arg) {
+ TestArgs* arg = reinterpret_cast<TestArgs*>(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<playground2::Instruction *> *visited,
- playground2::Instruction *instruction) {
+void TraverseRecursively(std::set<sandbox::Instruction*>* 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<Instruction *> visited;
+void CodeGen::Traverse(Instruction* instruction,
+ void (*fnc)(Instruction*, void*),
+ void* aux) {
+ std::set<Instruction*> visited;
TraverseRecursively(&visited, instruction);
- for (std::set<Instruction *>::const_iterator iter = visited.begin();
+ for (std::set<Instruction*>::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<const Instruction *> seen_instructions;
+ std::set<const Instruction*> 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<Instruction *>(insn));
+ stack.push_back(const_cast<Instruction*>(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<const Instruction *> seen_instructions;
+ BasicBlock* first_block = NULL;
+ std::set<const Instruction*> 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<TargetsToBlocks> less(*blocks, PointerCompare);
- typedef std::set<BasicBlock *, BasicBlock::Less<TargetsToBlocks> > Set;
+ typedef std::set<BasicBlock*, BasicBlock::Less<TargetsToBlocks> > 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<BasicBlock *> heads;
+ std::set<BasicBlock*> 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<Instruction *> Instructions;
-typedef std::vector<BasicBlock *> BasicBlocks;
-typedef std::map<const Instruction *, int> BranchTargets;
-typedef std::map<const Instruction *, BasicBlock *> TargetsToBlocks;
-typedef std::map<const BasicBlock *, int> IncomingBranches;
+typedef std::vector<Instruction*> Instructions;
+typedef std::vector<BasicBlock*> BasicBlocks;
+typedef std::map<const Instruction*, int> BranchTargets;
+typedef std::map<const Instruction*, BasicBlock*> TargetsToBlocks;
+typedef std::map<const BasicBlock*, int> 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<const BasicBlock *, int> 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<const BasicBlock *, int> 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<unsigned short>(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 <linux/filter.h>
- 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 <errno.h>
+
#include <algorithm>
#include <set>
#include <vector>
@@ -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 <time.h>
#include <unistd.h>
+#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 <string>
+#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<void *>(aux)),
+ aux_(const_cast<void*>(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 <errno.h>
+
#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 <stdint.h>
-
-namespace playground2 {
+namespace sandbox {
// The fields in this structure have the same meaning as the corresponding
// fields in "struct sock_filter". See <linux/filter.h> 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 <bool>
- 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 <time.h>
#include <unistd.h>
-#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<class T> int popcount(T x);
-template<> int popcount<unsigned int>(unsigned int x) {
+int popcount(uint32_t x) {
return __builtin_popcount(x);
}
-template<> int popcount<unsigned long>(unsigned long x) {
- return __builtin_popcountl(x);
-}
-template<> int popcount<unsigned long long>(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<bool *>(aux);
+void CheckForUnsafeErrorCodes(Instruction* insn, void* aux) {
+ bool* is_unsafe = static_cast<bool*>(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<Sandbox *>(aux);
+ SandboxBPF* sandbox = static_cast<SandboxBPF*>(aux);
if (BPF_CLASS(insn->code) == BPF_RET &&
(insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
insn->k = sandbox->Trap(ReturnErrno,
- reinterpret_cast<void *>(insn->k & SECCOMP_RET_DATA)).err();
+ reinterpret_cast<void*>(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<Sandbox::Evaluators *>(aux);
- const std::pair<Sandbox::EvaluateSyscall, void *>& 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<void *>(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<void*>(err.err() & SECCOMP_RET_DATA));
+ }
+ return err;
}
- return err;
-}
-intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) {
- SANDBOX_DIE(static_cast<char *>(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<char*>(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<unsigned short>(program->size()), bpf };
+ const struct sock_fprog prog = {static_cast<unsigned short>(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<uintptr_t>(SandboxSyscall(-1));
+ static_cast<uintptr_t>(SandboxSyscall(-1));
uint32_t low = static_cast<uint32_t>(syscall_entry_point);
#if __SIZEOF_POINTER__ > 4
- uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32);
+ uint32_t hi = static_cast<uint32_t>(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<const RedirectToUserSpacePolicyWrapper> 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<int>(sysnum), aux);
+ ErrorCode err = policy_->EvaluateSyscall(this, static_cast<int>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<intptr_t>(args.args[0]),
static_cast<intptr_t>(args.args[1]),
@@ -968,18 +999,24 @@ intptr_t Sandbox::ForwardSyscall(const struct arch_seccomp_data& args) {
static_cast<intptr_t>(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<char *>(msg));
+ErrorCode SandboxBPF::Kill(const char* msg) {
+ return Trap(BPFFailure, const_cast<char*>(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 <utility>
#include <vector>
+#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<std::pair<EvaluateSyscall, void *> >Evaluators;
-
+ typedef ErrorCode (*EvaluateSyscall)(SandboxBPF* sandbox_compiler,
+ int system_call_number,
+ void* aux);
+ typedef std::vector<std::pair<EvaluateSyscall, void*> > Evaluators;
// A vector of BPF instructions that need to be installed as a filter
// program in the kernel.
typedef std::vector<struct sock_filter> 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<Range> 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<const SandboxBPFPolicy> 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<BpfSandboxPolicy> 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<pid_t *>(aux);
+ pid_t* pid_ptr = static_cast<pid_t*>(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<int*>(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<int>::max() - kExpectedReturnValue - 1 >=
- static_cast<int>(MAX_PUBLIC_SYSCALL));
-
- for (int syscall_number = static_cast<int>(MIN_SYSCALL);
- syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
- ++syscall_number) {
- if (syscall_number == __NR_exit_group ||
- syscall_number == __NR_write) {
+ BPF_ASSERT(std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
+ static_cast<int>(MAX_PUBLIC_SYSCALL));
+
+ for (int syscall_number = static_cast<int>(MIN_SYSCALL);
+ syscall_number <= static_cast<int>(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<int>(__ARM_NR_set_tls + 1);
- syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
- ++syscall_number) {
+BPF_TEST(SandboxBPF, ArmPrivatePolicy, ArmPrivatePolicy) {
+ for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
+ syscall_number <= static_cast<int>(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<int *>(aux);
+ ++*reinterpret_cast<int*>(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<int>(args.args[1]) == -1) {
+intptr_t PrctlHandler(const struct arch_seccomp_data& args, void*) {
+ if (args.args[0] == PR_CAPBSET_DROP && static_cast<int>(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<std::string>()));
+ broker_process_.reset(
+ new BrokerProcess(EPERM, allowed_files, std::vector<std::string>()));
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<class BrokerProcess> 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<BrokerProcess*>(aux);
- switch(args.nr) {
+ switch (args.nr) {
case __NR_access:
return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
+ static_cast<int>(args.args[1]));
case __NR_open:
return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
+ static_cast<int>(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<int>(args.args[0]) == AT_FDCWD);
return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
+ static_cast<int>(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<InitializedOpenBroker*>(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<ArgValue *>::iterator iter = arg_values_.begin();
+ for (std::vector<ArgValue*>::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<ArgValue *> arg_values_;
+ std::vector<ArgValue*> 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<EqualityStressTest *>(aux)->Policy(sandbox, sysno);
+ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
+ return reinterpret_cast<EqualityStressTest*>(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<int *>(arg);
+static void* ThreadFnc(void* arg) {
+ ++*reinterpret_cast<int*>(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 <asm/unistd.h>
#include <errno.h>
-#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 <stdint.h>
-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<class T0 = intptr_t, class T1 = intptr_t, class T2 = intptr_t,
- class T3 = intptr_t, class T4 = intptr_t, class T5 = intptr_t>
-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<class T0, class T1, class T2, class T3, class T4, class T5>
+template <class T0 = intptr_t,
+ class T1 = intptr_t,
+ class T2 = intptr_t,
+ class T3 = intptr_t,
+ class T4 = intptr_t,
+ class T5 = intptr_t>
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 <class T0, class T1, class T2, class T3, class T4, class T5>
+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<class T0, class T1, class T2, class T3, class T4, class T5>
-inline intptr_t SandboxSyscall(int nr,
- T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
- __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3, class T4, class T5>
-inline intptr_t SandboxSyscall(int nr,
- T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+template <class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+ __attribute__((always_inline));
+template <class T0, class T1, class T2, class T3, class T4, class T5>
+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<class T0, class T1, class T2, class T3, class T4>
+template <class T0, class T1, class T2, class T3, class T4>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4)
- __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3, class T4>
+ __attribute__((always_inline));
+template <class T0, class T1, class T2, class T3, class T4>
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<class T0, class T1, class T2, class T3>
+template <class T0, class T1, class T2, class T3>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3)
- __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3>
+ __attribute__((always_inline));
+template <class T0, class T1, class T2, class T3>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
return SandboxSyscall(nr, p0, p1, p2, p3, 0, 0);
}
-template<class T0, class T1, class T2>
+template <class T0, class T1, class T2>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2)
- __attribute__((always_inline));
-template<class T0, class T1, class T2>
+ __attribute__((always_inline));
+template <class T0, class T1, class T2>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2) {
return SandboxSyscall(nr, p0, p1, p2, 0, 0, 0);
}
-template<class T0, class T1>
+template <class T0, class T1>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1)
- __attribute__((always_inline));
-template<class T0, class T1>
+ __attribute__((always_inline));
+template <class T0, class T1>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1) {
return SandboxSyscall(nr, p0, p1, 0, 0, 0, 0);
}
-template<class T0>
-inline intptr_t SandboxSyscall(int nr, T0 p0)
- __attribute__((always_inline));
-template<class T0>
+template <class T0>
+inline intptr_t SandboxSyscall(int nr, T0 p0) __attribute__((always_inline));
+template <class T0>
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 <stdint.h>
-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<uint64_t>* const seen_syscall_args =
static_cast<std::vector<uint64_t>*>(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<uint64_t> /* 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<char *>(
- SandboxSyscall(kMMapNr, (void *)NULL, 4096, PROT_READ,
- MAP_PRIVATE|MAP_ANONYMOUS, fd, 0L)));
+ char* addr0;
+ ASSERT_NE((char*)NULL,
+ addr0 = reinterpret_cast<char*>(
+ 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<char *>(
- 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<char*>(
+ 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<char *>(
- SandboxSyscall(kMMapNr, (void *)NULL, 8192L, PROT_READ,
- MAP_PRIVATE, fd, 0L)));
- ASSERT_NE((char *)NULL,
- addr3 = reinterpret_cast<char *>(
- SandboxSyscall(kMMapNr, (void *)NULL, 4096L, PROT_READ,
- MAP_PRIVATE, fd,
+ char* addr2, *addr3;
+ ASSERT_NE((char*)NULL,
+ addr2 = reinterpret_cast<char*>(SandboxSyscall(
+ kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
+ ASSERT_NE((char*)NULL,
+ addr3 = reinterpret_cast<char*>(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 <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
-#ifndef SECCOMP_BPF_STANDALONE
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#endif
+#include <limits>
+#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 <limits>
-
-
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<sigset_t*>(&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<ucontext_t *>(void_context);
+ ucontext_t* ctx = reinterpret_cast<ucontext_t*>(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<void *>(SECCOMP_IP(ctx)) ||
+ if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
sigsys.nr != static_cast<int>(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<uint64_t>(sigsys.ip),
- {
- static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM6(ctx))
- }
- };
+ sigsys.nr, SECCOMP_ARCH, reinterpret_cast<uint64_t>(sigsys.ip),
+ {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+ static_cast<uint64_t>(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<greg_t>(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 <map>
#include <vector>
-#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<TrapKey, uint16_t> 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 <string.h>
#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<struct sock_filter>& 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<struct sock_filter>& 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<struct sock_filter>& 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<const char *>(&state->data) + insn.k,
+ reinterpret_cast<const char*>(&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<struct sock_filter>& 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<int>(sysnum),
- static_cast<uint32_t>(SECCOMP_ARCH) };
+ struct arch_seccomp_data data = {static_cast<int>(sysnum),
+ static_cast<uint32_t>(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<struct sock_filter>& 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<struct sock_filter>& 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 <utility>
#include <vector>
+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<struct sock_filter>& 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<struct sock_filter>& 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 <vector>
#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<std::string>& allowed_r_files,
+BrokerProcess::BrokerProcess(int denied_errno,
+ const std::vector<std::string>& allowed_r_files,
const std::vector<std::string>& 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<std::string>& 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<int>::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<std::string>& allowed_r_files_,
- const std::vector<std::string>& allowed_w_files_,
+ explicit BrokerProcess(int denied_errno,
+ const std::vector<std::string>& allowed_r_files,
+ const std::vector<std::string>& 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<int>* 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<std::string> allowed_r_files_; // Files allowed for read.
const std::vector<std::string> 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 <vector>
#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<std::string> read_whitelist;
read_whitelist.push_back("/proc/cpuinfo");
- BrokerProcess* open_broker = new BrokerProcess(read_whitelist,
- std::vector<std::string>());
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, read_whitelist, std::vector<std::string>()));
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<std::string> 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<std::string> read_whitelist;
read_whitelist.push_back(kFileCpuInfo);
- BrokerProcess* open_broker = new BrokerProcess(read_whitelist,
- std::vector<std::string>(),
- fast_check_in_client);
+ scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(
+ EPERM, read_whitelist, std::vector<std::string>(), 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<std::string> 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<std::string> read_whitelist;
read_whitelist.push_back("/proc/cpuinfo");
- BrokerProcess open_broker(read_whitelist,
+ BrokerProcess open_broker(EPERM,
+ read_whitelist,
std::vector<std::string>(),
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<std::string> 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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<typeof(*((cap_t)0)), CapFreeDeleter> 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<char, CapTextFreeDeleter> 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<FILE, FILECloser> ScopedFILE;
+
+struct DIRCloser {
+ void operator()(DIR* d) const {
+ DCHECK(d);
+ PCHECK(0 == closedir(d));
+ }
+};
+
+typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
+
+COMPILE_ASSERT((base::is_same<uid_t, gid_t>::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/<tid>/fdinfo.
+void ChrootToThreadFdInfo(base::PlatformThreadId tid, bool* result) {
+ DCHECK(result);
+ *result = false;
+
+ COMPILE_ASSERT((base::is_same<base::PlatformThreadId, int>::value),
+ TidIsAnInt);
+ const std::string current_thread_fdinfo = "/proc/" +
+ base::IntToString(tid) + "/fdinfo/";
+
+ // Make extra sure that /proc/<tid>/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/<tid>/ directory
+// 2. We chroot to /proc/<tid>/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<std::string> 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<std::string> (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 <string>
+
+#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<std::string> 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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(".", &current));
+ 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<Credentials> 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 <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#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 <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#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 <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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<INT_PTR>(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<INT_PTR>(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 <string>
#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 <string>
+
+#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<base::char16> command;
command << std::hex << std::showbase << L"\"" << path <<
L"wow_helper.exe\" " << child_->ProcessId() << " " <<
bit_cast<ULONG>(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 <AccCtrl.h>
#include <windows.h>
#include "base/memory/scoped_ptr.h"
@@ -16,11 +17,11 @@ namespace sandbox {
bool GetDefaultDacl(HANDLE token,
scoped_ptr_malloc<TOKEN_DEFAULT_DACL>* 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 <vector>
#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 <string>
#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<std::wstring> dlls;
+ std::set<base::string16> dlls;
size_t buffer_bytes = 0;
std::list<InterceptionData>::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<InterceptionData>::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<base::string16> paths;
+ std::vector<base::string16> innerpaths;
+ base::SplitString(*name, '/', &paths);
+
+ for (std::vector<base::string16>::const_iterator iter = paths.begin();
+ iter != paths.end(); ++iter) {
+ base::SplitString(*iter, '\\', &innerpaths);
+ for (std::vector<base::string16>::const_iterator iter2 = innerpaths.begin();
+ iter2 != innerpaths.end(); ++iter2) {
+ if (*iter2 == L"..")
+ return true;
+ }
+ }
+
const wchar_t* pipe_name = name->c_str();
CountedParameterSet<NameBased> 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 <string>
#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 <list>
#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<wchar_t*>(cmd_line), NULL, NULL,
- FALSE, 0, NULL, NULL, &si, pi.Receive())) {
+ PROCESS_INFORMATION temp_process_info = {};
+ if (::CreateProcessW(exe_name, const_cast<wchar_t*>(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<char*>(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 <string>
#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<std::wstring> *exceptions) {
+ const std::vector<base::string16> *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 <vector>
#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<std::wstring> privilege_exceptions;
+ // std::vector<base::string16> privilege_exceptions;
// privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
// restricted_token.DeleteAllPrivileges(&privilege_exceptions);
unsigned DeleteAllPrivileges(
- const std::vector<std::wstring> *exceptions);
+ const std::vector<base::string16> *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<std::wstring> exceptions;
+ std::vector<base::string16> 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<std::wstring> privilege_exceptions;
+ std::vector<base::string16> privilege_exceptions;
std::vector<Sid> 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 <windows.h>
+
+#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<const char*>(source);
- char* to = reinterpret_cast<char*>(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 <string>
#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<RtlInitUnicodeStringFunction>(
- 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 <windows.h>
#include <string>
#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<ServiceFullThunk*>(
- local_thunk);
- ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
- remote_thunk);
+ ServiceFullThunk* full_local_thunk =
+ reinterpret_cast<ServiceFullThunk*>(local_thunk);
+ ServiceFullThunk* full_remote_thunk =
+ reinterpret_cast<ServiceFullThunk*>(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<char[]> 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<WinXpResolverTest*>(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<char[]> 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<std::wstring*>(args[i]);
+ delete reinterpret_cast<base::string16*>(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<std::wstring> data(new std::wstring);
+ scoped_ptr<base::string16> 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<CallbackGeneric>(&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<NameBased> 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<OpenEventParams> 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<NameBased> 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<OpenEventParams> 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<NameBased> 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<OpenEventParams> 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>(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 <string>
#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 <map>
-#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 <windows.h>
#include <string>
+
#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 <windows.h>
#include <string>
+#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);
}