summaryrefslogtreecommitdiffstats
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/sandbox
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/BUILD.gn14
-rw-r--r--chromium/sandbox/OWNERS9
-rw-r--r--chromium/sandbox/linux/BUILD.gn314
-rw-r--r--chromium/sandbox/linux/DEPS25
-rw-r--r--chromium/sandbox/linux/OWNERS3
-rw-r--r--chromium/sandbox/linux/sandbox_linux.gypi129
-rw-r--r--chromium/sandbox/linux/sandbox_linux_test_sources.gypi13
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/DEPS4
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc92
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h10
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc288
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc80
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h30
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc102
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h28
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc17
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h7
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/DEPS3
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h65
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/bpf_tests.h182
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc139
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen.cc116
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen.h3
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc251
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/demo.cc22
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/die.cc8
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/die.h3
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode.cc5
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode.h9
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc11
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h97
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc188
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h49
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h43
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.cc17
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h8
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc76
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h59
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc795
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.cc395
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.h197
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h3
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc137
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/trap.cc23
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/trap.h19
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/verifier.cc6
-rw-r--r--chromium/sandbox/linux/services/android_futex.h28
-rw-r--r--chromium/sandbox/linux/services/android_ucontext.h2
-rw-r--r--chromium/sandbox/linux/services/android_x86_64_ucontext.h88
-rw-r--r--chromium/sandbox/linux/services/broker_process.cc59
-rw-r--r--chromium/sandbox/linux/services/broker_process.h12
-rw-r--r--chromium/sandbox/linux/services/broker_process_unittest.cc131
-rw-r--r--chromium/sandbox/linux/services/credentials.cc89
-rw-r--r--chromium/sandbox/linux/services/credentials.h15
-rw-r--r--chromium/sandbox/linux/services/credentials_unittest.cc43
-rw-r--r--chromium/sandbox/linux/services/init_process_reaper.h4
-rw-r--r--chromium/sandbox/linux/services/libc_urandom_override.cc12
-rw-r--r--chromium/sandbox/linux/services/scoped_process.cc119
-rw-r--r--chromium/sandbox/linux/services/scoped_process.h55
-rw-r--r--chromium/sandbox/linux/services/scoped_process_unittest.cc130
-rw-r--r--chromium/sandbox/linux/services/thread_helpers.cc20
-rw-r--r--chromium/sandbox/linux/services/thread_helpers.h11
-rw-r--r--chromium/sandbox/linux/services/thread_helpers_unittests.cc24
-rw-r--r--chromium/sandbox/linux/services/unix_domain_socket_unittest.cc267
-rw-r--r--chromium/sandbox/linux/services/yama.cc116
-rw-r--r--chromium/sandbox/linux/services/yama.h58
-rw-r--r--chromium/sandbox/linux/services/yama_unittests.cc152
-rw-r--r--chromium/sandbox/linux/suid/client/DEPS3
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc144
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client.h59
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc13
-rw-r--r--chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h2
-rw-r--r--chromium/sandbox/linux/suid/linux_util.c15
-rw-r--r--chromium/sandbox/linux/suid/process_util.h6
-rw-r--r--chromium/sandbox/linux/suid/process_util_linux.c9
-rw-r--r--chromium/sandbox/linux/suid/sandbox.c93
-rw-r--r--chromium/sandbox/mac/BUILD.gn72
-rw-r--r--chromium/sandbox/mac/OWNERS2
-rw-r--r--chromium/sandbox/mac/bootstrap_sandbox.cc133
-rw-r--r--chromium/sandbox/mac/bootstrap_sandbox.h114
-rw-r--r--chromium/sandbox/mac/bootstrap_sandbox_unittest.mm417
-rw-r--r--chromium/sandbox/mac/launchd_interception_server.cc153
-rw-r--r--chromium/sandbox/mac/launchd_interception_server.h78
-rw-r--r--chromium/sandbox/mac/mach_message_server.cc184
-rw-r--r--chromium/sandbox/mac/mach_message_server.h95
-rw-r--r--chromium/sandbox/mac/os_compatibility.cc127
-rw-r--r--chromium/sandbox/mac/os_compatibility.h52
-rw-r--r--chromium/sandbox/mac/policy.cc56
-rw-r--r--chromium/sandbox/mac/policy.h70
-rw-r--r--chromium/sandbox/mac/policy_unittest.cc98
-rw-r--r--chromium/sandbox/mac/sandbox_mac.gypi100
-rw-r--r--chromium/sandbox/mac/xpc.h40
-rw-r--r--chromium/sandbox/mac/xpc_private_stubs.sig17
-rw-r--r--chromium/sandbox/mac/xpc_stubs.sig16
-rw-r--r--chromium/sandbox/mac/xpc_stubs_header.fragment27
-rw-r--r--chromium/sandbox/sandbox.gyp5
-rw-r--r--chromium/sandbox/sandbox_export.h29
-rw-r--r--chromium/sandbox/sandbox_linux_unittests.isolate27
-rw-r--r--chromium/sandbox/win/BUILD.gn285
-rw-r--r--chromium/sandbox/win/OWNERS3
-rw-r--r--chromium/sandbox/win/sandbox_win.gypi12
-rw-r--r--chromium/sandbox/win/src/Wow64.cc3
-rw-r--r--chromium/sandbox/win/src/acl.cc9
-rw-r--r--chromium/sandbox/win/src/acl.h5
-rw-r--r--chromium/sandbox/win/src/app_container.cc21
-rw-r--r--chromium/sandbox/win/src/app_container.h11
-rw-r--r--chromium/sandbox/win/src/app_container_test.cc3
-rw-r--r--chromium/sandbox/win/src/app_container_unittest.cc2
-rw-r--r--chromium/sandbox/win/src/broker_services.cc78
-rw-r--r--chromium/sandbox/win/src/broker_services.h8
-rw-r--r--chromium/sandbox/win/src/crosscall_server.cc1
-rw-r--r--chromium/sandbox/win/src/handle_closer.cc36
-rw-r--r--chromium/sandbox/win/src/handle_closer.h13
-rw-r--r--chromium/sandbox/win/src/handle_closer_agent.cc14
-rw-r--r--chromium/sandbox/win/src/handle_closer_test.cc14
-rw-r--r--chromium/sandbox/win/src/handle_dispatcher.cc6
-rw-r--r--chromium/sandbox/win/src/handle_policy.cc3
-rw-r--r--chromium/sandbox/win/src/handle_policy.h1
-rw-r--r--chromium/sandbox/win/src/handle_table.cc38
-rw-r--r--chromium/sandbox/win/src/handle_table.h42
-rw-r--r--chromium/sandbox/win/src/interception.cc5
-rw-r--r--chromium/sandbox/win/src/interceptors.h7
-rw-r--r--chromium/sandbox/win/src/interceptors_64.cc41
-rw-r--r--chromium/sandbox/win/src/interceptors_64.h23
-rw-r--r--chromium/sandbox/win/src/ipc_tags.h3
-rw-r--r--chromium/sandbox/win/src/ipc_unittest.cc10
-rw-r--r--chromium/sandbox/win/src/job.cc16
-rw-r--r--chromium/sandbox/win/src/job.h5
-rw-r--r--chromium/sandbox/win/src/job_unittest.cc29
-rw-r--r--chromium/sandbox/win/src/named_pipe_dispatcher.cc4
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy_test.cc7
-rw-r--r--chromium/sandbox/win/src/nt_internals.h10
-rw-r--r--chromium/sandbox/win/src/policy_engine_opcodes.cc2
-rw-r--r--chromium/sandbox/win/src/policy_engine_unittest.cc2
-rw-r--r--chromium/sandbox/win/src/policy_low_level_unittest.cc18
-rw-r--r--chromium/sandbox/win/src/policy_opcodes_unittest.cc7
-rw-r--r--chromium/sandbox/win/src/policy_target_test.cc4
-rw-r--r--chromium/sandbox/win/src/process_mitigations.cc20
-rw-r--r--chromium/sandbox/win/src/process_mitigations_test.cc57
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc57
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h31
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_interception.cc29
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_interception.h46
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_policy.cc24
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_policy.h35
-rw-r--r--chromium/sandbox/win/src/process_policy_test.cc48
-rw-r--r--chromium/sandbox/win/src/process_thread_interception.cc46
-rw-r--r--chromium/sandbox/win/src/process_thread_interception.h13
-rw-r--r--chromium/sandbox/win/src/process_thread_policy.cc3
-rw-r--r--chromium/sandbox/win/src/restricted_token.cc1
-rw-r--r--chromium/sandbox/win/src/restricted_token_unittest.cc13
-rw-r--r--chromium/sandbox/win/src/restricted_token_utils.cc2
-rw-r--r--chromium/sandbox/win/src/sandbox.cc8
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.cc13
-rw-r--r--chromium/sandbox/win/src/sandbox_policy.h24
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.cc63
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.h18
-rw-r--r--chromium/sandbox/win/src/sandbox_types.h1
-rw-r--r--chromium/sandbox/win/src/sandbox_utils.cc8
-rw-r--r--chromium/sandbox/win/src/sandbox_utils.h3
-rw-r--r--chromium/sandbox/win/src/security_level.h3
-rw-r--r--chromium/sandbox/win/src/service_resolver.h32
-rw-r--r--chromium/sandbox/win/src/service_resolver_32.cc46
-rw-r--r--chromium/sandbox/win/src/service_resolver_64.cc31
-rw-r--r--chromium/sandbox/win/src/service_resolver_unittest.cc4
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_client.cc4
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_server.cc28
-rw-r--r--chromium/sandbox/win/src/target_process.cc2
-rw-r--r--chromium/sandbox/win/src/target_process.h2
-rw-r--r--chromium/sandbox/win/src/win_utils.cc31
-rw-r--r--chromium/sandbox/win/src/window.cc16
-rw-r--r--chromium/sandbox/win/wow_helper/wow_helper.cc37
172 files changed, 7619 insertions, 1766 deletions
diff --git a/chromium/sandbox/BUILD.gn b/chromium/sandbox/BUILD.gn
new file mode 100644
index 00000000000..e3a8b624d59
--- /dev/null
+++ b/chromium/sandbox/BUILD.gn
@@ -0,0 +1,14 @@
+# 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.
+
+# Meta-target that forwards to the proper platform one.
+group("sandbox") {
+ if (is_win) {
+ deps = [ "//sandbox/win:sandbox" ]
+ } else if (is_mac) {
+ deps = [ "//sandbox/mac:sandbox" ]
+ } else if (is_linux || is_android) {
+ deps = [ "//sandbox/linux:sandbox" ]
+ }
+}
diff --git a/chromium/sandbox/OWNERS b/chromium/sandbox/OWNERS
index 5d15856389d..5d3f6ff9e17 100644
--- a/chromium/sandbox/OWNERS
+++ b/chromium/sandbox/OWNERS
@@ -1,10 +1,3 @@
-# For Windows:
cpu@chromium.org
-jschuh@chromium.org
-nsylvain@chromium.org
-rvargas@chromium.org
-# For Linux:
-markus@chromium.org
jln@chromium.org
-cevans@chromium.org
-jorgelo@chromium.org
+jschuh@chromium.org
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn
new file mode 100644
index 00000000000..0cc9be4562a
--- /dev/null
+++ b/chromium/sandbox/linux/BUILD.gn
@@ -0,0 +1,314 @@
+# 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.
+
+import("//build/config/features.gni")
+
+declare_args() {
+ compile_suid_client = is_linux
+
+ compile_credentials = is_linux
+
+ compile_seccomp_bpf_demo =
+ (is_linux && (cpu_arch == "x86" || cpu_arch == "x64"))
+}
+
+# We have two principal targets: sandbox and sandbox_linux_unittests
+# All other targets are listed as dependencies.
+# There is one notable exception: for historical reasons, chrome_sandbox is
+# the setuid sandbox and is its own target.
+
+group("sandbox") {
+ deps = [
+ ":sandbox_services",
+ ]
+
+ if (compile_suid_client) {
+ deps += [ ":suid_sandbox_client" ]
+ }
+ if (use_seccomp_bpf) {
+ deps += [
+ ":seccomp_bpf",
+ ":seccomp_bpf_helpers",
+ ]
+ }
+}
+
+source_set("sandbox_linux_test_utils") {
+ sources = [
+ "tests/sandbox_test_runner.cc",
+ "tests/sandbox_test_runner.h",
+ "tests/sandbox_test_runner_function_pointer.cc",
+ "tests/sandbox_test_runner_function_pointer.h",
+ "tests/test_utils.cc",
+ "tests/test_utils.h",
+ "tests/unit_tests.cc",
+ "tests/unit_tests.h",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ if (use_seccomp_bpf) {
+ sources += [
+ "seccomp-bpf/bpf_tester_compatibility_delegate.h",
+ "seccomp-bpf/bpf_tests.h",
+ "seccomp-bpf/sandbox_bpf_test_runner.cc",
+ "seccomp-bpf/sandbox_bpf_test_runner.h",
+ ]
+ deps += [
+ ":seccomp_bpf",
+ ]
+ }
+}
+
+# The main sandboxing test target.
+test("sandbox_linux_unittests") {
+ sources = [
+ "tests/main.cc",
+ "tests/unit_tests_unittest.cc",
+ "services/broker_process_unittest.cc",
+ "services/scoped_process_unittest.cc",
+ "services/thread_helpers_unittests.cc",
+ "services/yama_unittests.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ ":sandbox_linux_test_utils",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+
+ if (compile_suid_client) {
+ sources += [
+ "suid/client/setuid_sandbox_client_unittest.cc",
+ ]
+ }
+ if (use_seccomp_bpf) {
+ sources += [
+ "seccomp-bpf-helpers/baseline_policy_unittest.cc",
+ "seccomp-bpf/bpf_tests_unittest.cc",
+ "seccomp-bpf/codegen_unittest.cc",
+ "seccomp-bpf/errorcode_unittest.cc",
+ "seccomp-bpf/sandbox_bpf_unittest.cc",
+ "seccomp-bpf/syscall_iterator_unittest.cc",
+ "seccomp-bpf/syscall_unittest.cc",
+ ]
+ }
+ if (compile_credentials) {
+ sources += [
+ "services/credentials_unittest.cc",
+ "services/unix_domain_socket_unittest.cc",
+ ]
+ }
+}
+
+# TODO(GYP) Android version of this test.
+# {
+# # This target is the shared library used by Android APK (i.e.
+# # JNI-friendly) tests.
+# "target_name": "sandbox_linux_jni_unittests",
+# "includes": [
+# "sandbox_linux_test_sources.gypi",
+# ],
+# "type": "shared_library",
+# "conditions": [
+# [ "OS == "android"", {
+# "dependencies": [
+# "../testing/android/native_test.gyp:native_test_native_code",
+# ],
+# }],
+# ],
+# },
+
+component("seccomp_bpf") {
+ sources = [
+ "seccomp-bpf/basicblock.cc",
+ "seccomp-bpf/basicblock.h",
+ "seccomp-bpf/codegen.cc",
+ "seccomp-bpf/codegen.h",
+ "seccomp-bpf/die.cc",
+ "seccomp-bpf/die.h",
+ "seccomp-bpf/errorcode.cc",
+ "seccomp-bpf/errorcode.h",
+ "seccomp-bpf/instruction.h",
+ "seccomp-bpf/linux_seccomp.h",
+ "seccomp-bpf/sandbox_bpf.cc",
+ "seccomp-bpf/sandbox_bpf.h",
+ "seccomp-bpf/sandbox_bpf_compatibility_policy.h",
+ "seccomp-bpf/sandbox_bpf_policy.cc",
+ "seccomp-bpf/sandbox_bpf_policy.h",
+ "seccomp-bpf/syscall.cc",
+ "seccomp-bpf/syscall.h",
+ "seccomp-bpf/syscall_iterator.cc",
+ "seccomp-bpf/syscall_iterator.h",
+ "seccomp-bpf/trap.cc",
+ "seccomp-bpf/trap.h",
+ "seccomp-bpf/verifier.cc",
+ "seccomp-bpf/verifier.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services_headers",
+ "//base",
+ ]
+}
+
+component("seccomp_bpf_helpers") {
+ 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",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ ":seccomp_bpf",
+ ]
+}
+
+if (compile_seccomp_bpf_demo) {
+ # A demonstration program for the seccomp-bpf sandbox.
+ executable("seccomp_bpf_demo") {
+ sources = [
+ "seccomp-bpf/demo.cc",
+ ]
+ deps = [
+ ":seccomp_bpf",
+ ]
+ }
+}
+
+# The setuid sandbox for Linux.
+executable("chrome_sandbox") {
+ sources = [
+ "suid/common/sandbox.h",
+ "suid/common/suid_unsafe_environment_variables.h",
+ "suid/linux_util.c",
+ "suid/linux_util.h",
+ "suid/process_util.h",
+ "suid/process_util_linux.c",
+ "suid/sandbox.c",
+ ]
+
+ cflags = [
+ # For ULLONG_MAX
+ "-std=gnu99",
+ # These files have a suspicious comparison.
+ # TODO fix this and re-enable this warning.
+ "-Wno-sign-compare",
+ ]
+}
+
+component("sandbox_services") {
+ sources = [
+ "services/broker_process.cc",
+ "services/broker_process.h",
+ "services/init_process_reaper.cc",
+ "services/init_process_reaper.h",
+ "services/scoped_process.cc",
+ "services/scoped_process.h",
+ "services/thread_helpers.cc",
+ "services/thread_helpers.h",
+ "services/yama.h",
+ "services/yama.cc",
+ ]
+
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ if (compile_credentials) {
+ sources += [
+ "services/credentials.cc",
+ "services/credentials.h",
+ ]
+ # For capabilities.cc.
+ configs += [ "//build/config/linux:libcap" ]
+ }
+
+ deps = [
+ "//base",
+ ]
+}
+
+source_set("sandbox_services_headers") {
+ sources = [
+ "services/android_arm_ucontext.h",
+ "services/android_futex.h",
+ "services/android_ucontext.h",
+ "services/android_i386_ucontext.h",
+ "services/arm_linux_syscalls.h",
+ "services/linux_syscalls.h",
+ "services/x86_32_linux_syscalls.h",
+ "services/x86_64_linux_syscalls.h",
+ ]
+}
+
+# We make this its own target so that it does not interfere with our tests.
+source_set("libc_urandom_override") {
+ sources = [
+ "services/libc_urandom_override.cc",
+ "services/libc_urandom_override.h",
+ ]
+ deps = [
+ "//base",
+ ]
+}
+
+component("suid_sandbox_client") {
+ sources = [
+ "suid/common/sandbox.h",
+ "suid/common/suid_unsafe_environment_variables.h",
+ "suid/client/setuid_sandbox_client.cc",
+ "suid/client/setuid_sandbox_client.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services",
+ "//base",
+ ]
+}
+
+if (is_android) {
+ # TODO(GYP) enable this. Needs an android_strip wrapper python script.
+ #action("sandbox_linux_unittests_stripped") {
+ # script = "android_stip.py"
+ #
+ # in_file = "$root_out_dir/sandbox_linux_unittests"
+ #
+ # out_file = "$root_out_dir/sandbox_linux_unittests_stripped"
+ # outputs = [ out_file ]
+ #
+ # args = [
+ # rebase_path(in_file, root_build_dir),
+ # "-o", rebase_path(out_file, root_build_dir),
+ # ]
+ #
+ # deps = [
+ # ":sandbox_linux_unittests",
+ # ]
+ #}
+
+ # TODO(GYP) convert this.
+ # {
+ # 'target_name': 'sandbox_linux_jni_unittests_apk',
+ # 'type': 'none',
+ # 'variables': {
+ # 'test_suite_name': 'sandbox_linux_jni_unittests',
+ # },
+ # 'dependencies': [
+ # 'sandbox_linux_jni_unittests',
+ # ],
+ # 'includes': [ '../../build/apk_test.gypi' ],
+ # }
+}
diff --git a/chromium/sandbox/linux/DEPS b/chromium/sandbox/linux/DEPS
new file mode 100644
index 00000000000..39128593449
--- /dev/null
+++ b/chromium/sandbox/linux/DEPS
@@ -0,0 +1,25 @@
+include_rules = [
+ # First, exclude everything.
+ # Exclude a few dependencies that are included in the root DEPS and that we
+ # don't need.
+ # Sadly, there is no way to exclude all root DEPS since the root has no name.
+ "-ipc",
+ "-library_loaders",
+ "-third_party",
+ "-url",
+ # Make sure that each subdirectory has to declare its dependencies in
+ # sandbox/ explicitly.
+ "-sandbox/linux",
+
+ # Second, add what we want to allow.
+ # Anything included from sandbox/linux must be declared after this line or in
+ # a more specific DEPS file.
+ # base/, build/ and testing/ are already included in the global DEPS file,
+ # but be explicit.
+ "+base",
+ "+build",
+ "+testing",
+ "+sandbox/sandbox_export.h",
+ # Everyone can use tests/
+ "+sandbox/linux/tests",
+]
diff --git a/chromium/sandbox/linux/OWNERS b/chromium/sandbox/linux/OWNERS
new file mode 100644
index 00000000000..35643d1565f
--- /dev/null
+++ b/chromium/sandbox/linux/OWNERS
@@ -0,0 +1,3 @@
+cevans@chromium.org
+jln@chromium.org
+jorgelo@chromium.org
diff --git a/chromium/sandbox/linux/sandbox_linux.gypi b/chromium/sandbox/linux/sandbox_linux.gypi
index 0e211f6c320..9ddcf0c874c 100644
--- a/chromium/sandbox/linux/sandbox_linux.gypi
+++ b/chromium/sandbox/linux/sandbox_linux.gypi
@@ -12,13 +12,6 @@
'compile_suid_client': 0,
'compile_credentials': 0,
}],
- ['((OS=="linux" or OS=="android") and '
- '(target_arch=="ia32" or target_arch=="x64" or '
- 'target_arch=="arm"))', {
- 'compile_seccomp_bpf': 1,
- }, {
- 'compile_seccomp_bpf': 0,
- }],
['OS=="linux" and (target_arch=="ia32" or target_arch=="x64")', {
'compile_seccomp_bpf_demo': 1,
}, {
@@ -40,8 +33,8 @@
'targets': [
# We have two principal targets: sandbox and sandbox_linux_unittests
# All other targets are listed as dependencies.
- # FIXME(jln): for historial reasons, sandbox_linux is the setuid sandbox
- # and is its own target.
+ # There is one notable exception: for historical reasons, chrome_sandbox is
+ # the setuid sandbox and is its own target.
{
'target_name': 'sandbox',
'type': 'none',
@@ -55,7 +48,7 @@
],
}],
# Compile seccomp BPF when we support it.
- [ 'compile_seccomp_bpf==1', {
+ [ 'use_seccomp_bpf==1', {
'dependencies': [
'seccomp_bpf',
'seccomp_bpf_helpers',
@@ -64,6 +57,39 @@
],
},
{
+ 'target_name': 'sandbox_linux_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'tests/sandbox_test_runner.cc',
+ 'tests/sandbox_test_runner.h',
+ 'tests/sandbox_test_runner_function_pointer.cc',
+ 'tests/sandbox_test_runner_function_pointer.h',
+ 'tests/test_utils.cc',
+ 'tests/test_utils.h',
+ 'tests/unit_tests.cc',
+ 'tests/unit_tests.h',
+ ],
+ 'conditions': [
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'seccomp-bpf/bpf_tester_compatibility_delegate.h',
+ 'seccomp-bpf/bpf_tests.h',
+ 'seccomp-bpf/sandbox_bpf_test_runner.cc',
+ 'seccomp-bpf/sandbox_bpf_test_runner.h',
+ ],
+ 'dependencies': [
+ 'seccomp_bpf',
+ ]
+ }],
+ ],
+ },
+ {
# The main sandboxing test target.
'target_name': 'sandbox_linux_unittests',
'includes': [
@@ -80,21 +106,16 @@
],
'type': 'shared_library',
'conditions': [
- [ 'OS == "android" and gtest_target_type == "shared_library"', {
+ [ 'OS == "android"', {
'dependencies': [
'../testing/android/native_test.gyp:native_test_native_code',
],
- 'ldflags!': [
- # Remove warnings about text relocations, to prevent build
- # failure.
- '-Wl,--warn-shared-textrel'
- ],
}],
],
},
{
'target_name': 'seccomp_bpf',
- 'type': 'static_library',
+ 'type': '<(component)',
'sources': [
'seccomp-bpf/basicblock.cc',
'seccomp-bpf/basicblock.h',
@@ -108,6 +129,8 @@
'seccomp-bpf/linux_seccomp.h',
'seccomp-bpf/sandbox_bpf.cc',
'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/sandbox_bpf_compatibility_policy.h',
+ 'seccomp-bpf/sandbox_bpf_policy.cc',
'seccomp-bpf/sandbox_bpf_policy.h',
'seccomp-bpf/syscall.cc',
'seccomp-bpf/syscall.h',
@@ -122,13 +145,16 @@
'../base/base.gyp:base',
'sandbox_services_headers',
],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
'include_dirs': [
'../..',
],
},
{
'target_name': 'seccomp_bpf_helpers',
- 'type': 'static_library',
+ 'type': '<(component)',
'sources': [
'seccomp-bpf-helpers/baseline_policy.cc',
'seccomp-bpf-helpers/baseline_policy.h',
@@ -140,6 +166,11 @@
'seccomp-bpf-helpers/syscall_sets.h',
],
'dependencies': [
+ '../base/base.gyp:base',
+ 'seccomp_bpf',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
],
'include_dirs': [
'../..',
@@ -185,20 +216,34 @@
'include_dirs': [
'../..',
],
+ # Do not use any sanitizer tools with this binary. http://crbug.com/382766
+ 'cflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ 'ldflags/': [
+ ['exclude', '-fsanitize'],
+ ],
},
{ 'target_name': 'sandbox_services',
- 'type': 'static_library',
+ 'type': '<(component)',
'sources': [
'services/broker_process.cc',
'services/broker_process.h',
'services/init_process_reaper.cc',
'services/init_process_reaper.h',
+ 'services/scoped_process.cc',
+ 'services/scoped_process.h',
'services/thread_helpers.cc',
'services/thread_helpers.h',
+ 'services/yama.h',
+ 'services/yama.cc',
],
'dependencies': [
'../base/base.gyp:base',
],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
'conditions': [
['compile_credentials==1', {
'sources': [
@@ -219,6 +264,7 @@
'type': 'none',
'sources': [
'services/android_arm_ucontext.h',
+ 'services/android_futex.h',
'services/android_ucontext.h',
'services/android_i386_ucontext.h',
'services/arm_linux_syscalls.h',
@@ -248,13 +294,16 @@
},
{
'target_name': 'suid_sandbox_client',
- 'type': 'static_library',
+ 'type': '<(component)',
'sources': [
'suid/common/sandbox.h',
'suid/common/suid_unsafe_environment_variables.h',
'suid/client/setuid_sandbox_client.cc',
'suid/client/setuid_sandbox_client.h',
],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
'dependencies': [
'../base/base.gyp:base',
'sandbox_services',
@@ -265,18 +314,28 @@
},
],
'conditions': [
- # Strategy copied from base_unittests_apk in base/base.gyp.
- [ 'OS=="android" and gtest_target_type == "shared_library"', {
+ [ 'OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_stripped',
+ 'type': 'none',
+ 'dependencies': [ 'sandbox_linux_unittests' ],
+ 'actions': [{
+ 'action_name': 'strip sandbox_linux_unittests',
+ 'inputs': [ '<(PRODUCT_DIR)/sandbox_linux_unittests' ],
+ 'outputs': [ '<(PRODUCT_DIR)/sandbox_linux_unittests_stripped' ],
+ 'action': [ '<(android_strip)', '<@(_inputs)', '-o', '<@(_outputs)' ],
+ }],
+ }
+ ],
+ }],
+ [ 'OS=="android"', {
'targets': [
{
'target_name': 'sandbox_linux_jni_unittests_apk',
'type': 'none',
'variables': {
'test_suite_name': 'sandbox_linux_jni_unittests',
- 'input_shlib_path':
- '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)'
- 'sandbox_linux_jni_unittests'
- '<(SHARED_LIB_SUFFIX)',
},
'dependencies': [
'sandbox_linux_jni_unittests',
@@ -285,5 +344,23 @@
}
],
}],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ '../sandbox_linux_unittests.isolate',
+ ],
+ 'sources': [
+ '../sandbox_linux_unittests.isolate',
+ ],
+ },
+ ],
+ }],
],
}
diff --git a/chromium/sandbox/linux/sandbox_linux_test_sources.gypi b/chromium/sandbox/linux/sandbox_linux_test_sources.gypi
index a6a916fee45..bf414712132 100644
--- a/chromium/sandbox/linux/sandbox_linux_test_sources.gypi
+++ b/chromium/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -7,7 +7,9 @@
{
'dependencies': [
'sandbox',
+ 'sandbox_linux_test_utils',
'../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
],
'include_dirs': [
@@ -15,10 +17,11 @@
],
'sources': [
'tests/main.cc',
- 'tests/unit_tests.cc',
- 'tests/unit_tests.h',
+ 'tests/unit_tests_unittest.cc',
'services/broker_process_unittest.cc',
+ 'services/scoped_process_unittest.cc',
'services/thread_helpers_unittests.cc',
+ 'services/yama_unittests.cc',
],
'conditions': [
[ 'compile_suid_client==1', {
@@ -26,9 +29,10 @@
'suid/client/setuid_sandbox_client_unittest.cc',
],
}],
- [ 'compile_seccomp_bpf==1', {
+ [ 'use_seccomp_bpf==1', {
'sources': [
- 'seccomp-bpf/bpf_tests.h',
+ 'seccomp-bpf-helpers/baseline_policy_unittest.cc',
+ 'seccomp-bpf/bpf_tests_unittest.cc',
'seccomp-bpf/codegen_unittest.cc',
'seccomp-bpf/errorcode_unittest.cc',
'seccomp-bpf/sandbox_bpf_unittest.cc',
@@ -39,6 +43,7 @@
[ 'compile_credentials==1', {
'sources': [
'services/credentials_unittest.cc',
+ 'services/unix_domain_socket_unittest.cc',
],
}],
],
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/DEPS b/chromium/sandbox/linux/seccomp-bpf-helpers/DEPS
new file mode 100644
index 00000000000..e8000d3b32c
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+sandbox/linux/services",
+ "+sandbox/linux/seccomp-bpf",
+]
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index d0e53e39bc8..a9fb1044778 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -6,8 +6,10 @@
#include <errno.h>
#include <sys/mman.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "base/logging.h"
#include "build/build_config.h"
@@ -30,19 +32,17 @@ bool IsBaselinePolicyAllowed(int sysno) {
SyscallSets::IsAllowedBasicScheduler(sysno) ||
SyscallSets::IsAllowedEpoll(sysno) ||
SyscallSets::IsAllowedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsAllowedFutex(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);
}
@@ -63,14 +63,15 @@ bool IsBaselinePolicyWatched(int sysno) {
SyscallSets::IsInotify(sysno) ||
SyscallSets::IsKernelModule(sysno) ||
SyscallSets::IsKeyManagement(sysno) ||
+ SyscallSets::IsKill(sysno) ||
SyscallSets::IsMessageQueue(sysno) ||
SyscallSets::IsMisc(sysno) ||
#if defined(__x86_64__)
SyscallSets::IsNetworkSocketInformation(sysno) ||
#endif
SyscallSets::IsNuma(sysno) ||
+ SyscallSets::IsPrctl(sysno) ||
SyscallSets::IsProcessGroupOrSession(sysno) ||
- SyscallSets::IsProcessPrivilegeChange(sysno) ||
#if defined(__i386__)
SyscallSets::IsSocketCall(sysno) ||
#endif
@@ -81,22 +82,48 @@ bool IsBaselinePolicyWatched(int sysno) {
}
// |fs_denied_errno| is the errno return for denied filesystem access.
-ErrorCode EvaluateSyscallImpl(int fs_denied_errno, SandboxBPF* sandbox,
+ErrorCode EvaluateSyscallImpl(int fs_denied_errno,
+ pid_t current_pid,
+ SandboxBPF* sandbox,
int sysno) {
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ // TCGETS is required by the sanitizers on failure.
+ if (sysno == __NR_ioctl) {
+ return RestrictIoctl(sandbox);
+ }
+
+ if (sysno == __NR_sched_getaffinity) {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+
+ if (sysno == __NR_sigaltstack) {
+ // Required for better stack overflow detection in ASan. Disallowed in
+ // non-ASan builds.
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+#endif // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
+ // defined(MEMORY_SANITIZER)
+
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));
+ if (sysno == __NR_clone) {
+ return RestrictCloneToThreadsAndEPERMFork(sandbox);
}
+
+ if (sysno == __NR_fcntl)
+ return RestrictFcntlCommands(sandbox);
+
+#if defined(__i386__) || defined(__arm__)
+ if (sysno == __NR_fcntl64)
+ return RestrictFcntlCommands(sandbox);
#endif
+ if (sysno == __NR_futex)
+ return RestrictFutex(sandbox);
+
if (sysno == __NR_madvise) {
// Only allow MADV_DONTNEED (aka MADV_FREE).
return sandbox->Cond(2, ErrorCode::TP_32BIT,
@@ -118,14 +145,23 @@ ErrorCode EvaluateSyscallImpl(int fs_denied_errno, SandboxBPF* sandbox,
if (sysno == __NR_mprotect)
return RestrictMprotectFlags(sandbox);
- if (sysno == __NR_fcntl)
- return RestrictFcntlCommands(sandbox);
+ if (sysno == __NR_prctl)
+ return sandbox::RestrictPrctl(sandbox);
-#if defined(__i386__) || defined(__arm__)
- if (sysno == __NR_fcntl64)
- return RestrictFcntlCommands(sandbox);
+#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 (SyscallSets::IsKill(sysno)) {
+ return RestrictKillTarget(current_pid, sandbox, sysno);
+ }
+
if (SyscallSets::IsFileSystem(sysno) ||
SyscallSets::IsCurrentDirectory(sysno)) {
return ErrorCode(fs_denied_errno);
@@ -137,7 +173,8 @@ ErrorCode EvaluateSyscallImpl(int fs_denied_errno, SandboxBPF* sandbox,
if (SyscallSets::IsUmask(sysno) ||
SyscallSets::IsDeniedFileSystemAccessViaFd(sysno) ||
- SyscallSets::IsDeniedGetOrModifySocket(sysno)) {
+ SyscallSets::IsDeniedGetOrModifySocket(sysno) ||
+ SyscallSets::IsProcessPrivilegeChange(sysno)) {
return ErrorCode(EPERM);
}
@@ -151,6 +188,7 @@ ErrorCode EvaluateSyscallImpl(int fs_denied_errno, SandboxBPF* sandbox,
// 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);
}
@@ -160,16 +198,24 @@ ErrorCode EvaluateSyscallImpl(int fs_denied_errno, SandboxBPF* sandbox,
// Unfortunately C++03 doesn't allow delegated constructors.
// Call other constructor when C++11 lands.
BaselinePolicy::BaselinePolicy()
- : fs_denied_errno_(EPERM) {}
+ : fs_denied_errno_(EPERM), current_pid_(syscall(__NR_getpid)) {}
BaselinePolicy::BaselinePolicy(int fs_denied_errno)
- : fs_denied_errno_(fs_denied_errno) {}
+ : fs_denied_errno_(fs_denied_errno), current_pid_(syscall(__NR_getpid)) {}
-BaselinePolicy::~BaselinePolicy() {}
+BaselinePolicy::~BaselinePolicy() {
+ // Make sure that this policy is created, used and destroyed by a single
+ // process.
+ DCHECK_EQ(syscall(__NR_getpid), current_pid_);
+}
ErrorCode BaselinePolicy::EvaluateSyscall(SandboxBPF* sandbox,
int sysno) const {
- return EvaluateSyscallImpl(fs_denied_errno_, sandbox, sysno);
+ // Make sure that this policy is used in the creating process.
+ if (1 == sysno) {
+ DCHECK_EQ(syscall(__NR_getpid), current_pid_);
+ }
+ return EvaluateSyscallImpl(fs_denied_errno_, current_pid_, 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
index 1dfd137fa3d..edf4c77b3c6 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
@@ -7,6 +7,7 @@
#include "sandbox/linux/seccomp-bpf/errorcode.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -17,12 +18,14 @@ class SandboxBPFPolicy;
// 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.
+// This class implements the SandboxBPFPolicy interface with a "baseline"
+// policy for us 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 {
+// A baseline policy is only valid for the process for which this object was
+// instantiated (so do not fork() and use it in a child).
+class SANDBOX_EXPORT BaselinePolicy : public SandboxBPFPolicy {
public:
BaselinePolicy();
// |fs_denied_errno| is the errno returned when a filesystem access system
@@ -35,6 +38,7 @@ class BaselinePolicy : public SandboxBPFPolicy {
private:
int fs_denied_errno_;
+ pid_t current_pid_;
DISALLOW_COPY_AND_ASSIGN(BaselinePolicy);
};
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
new file mode 100644
index 00000000000..2fa0e93c1f0
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -0,0 +1,288 @@
+// 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/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <linux/futex.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/android_futex.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// |pid| is the return value of a fork()-like call. This
+// makes sure that if fork() succeeded the child exits
+// and the parent waits for it.
+void HandlePostForkReturn(pid_t pid) {
+ const int kChildExitCode = 1;
+ if (pid > 0) {
+ int status = 0;
+ PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kChildExitCode, WEXITSTATUS(status));
+ } else if (pid == 0) {
+ _exit(kChildExitCode);
+ }
+}
+
+// Check that HandlePostForkReturn works.
+TEST(BaselinePolicy, HandlePostForkReturn) {
+ pid_t pid = fork();
+ HandlePostForkReturn(pid);
+}
+
+// This also tests that read(), write() and fstat() are allowed.
+void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) {
+ BPF_ASSERT_LE(0, read_end.get());
+ BPF_ASSERT_LE(0, write_end.get());
+ struct stat stat_buf;
+ int sys_ret = fstat(read_end.get(), &stat_buf);
+ BPF_ASSERT_EQ(0, sys_ret);
+ BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode));
+
+ const ssize_t kTestTransferSize = 4;
+ static const char kTestString[kTestTransferSize] = {'T', 'E', 'S', 'T'};
+ ssize_t transfered = 0;
+
+ transfered =
+ HANDLE_EINTR(write(write_end.get(), kTestString, kTestTransferSize));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ char read_buf[kTestTransferSize + 1] = {0};
+ transfered = HANDLE_EINTR(read(read_end.get(), read_buf, sizeof(read_buf)));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ BPF_ASSERT_EQ(0, memcmp(kTestString, read_buf, kTestTransferSize));
+}
+
+// Test that a few easy-to-test system calls are allowed.
+BPF_TEST_C(BaselinePolicy, BaselinePolicyBasicAllowed, BaselinePolicy) {
+ BPF_ASSERT_EQ(0, sched_yield());
+
+ int pipefd[2];
+ int sys_ret = pipe(pipefd);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(pipefd[0]), base::ScopedFD(pipefd[1]));
+
+ BPF_ASSERT_LE(1, getpid());
+ BPF_ASSERT_LE(0, getuid());
+}
+
+BPF_TEST_C(BaselinePolicy, FchmodErrno, BaselinePolicy) {
+ int ret = fchmod(-1, 07777);
+ BPF_ASSERT_EQ(-1, ret);
+ // Without the sandbox, this would EBADF instead.
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkErrno, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = fork();
+ const int fork_errno = errno;
+ HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkX86Glibc() {
+ return syscall(__NR_clone, CLONE_PARENT_SETTID | SIGCHLD);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkX86Glibc();
+ const int fork_errno = errno;
+ HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkARMGlibc() {
+ return syscall(__NR_clone,
+ CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkARMGlibc();
+ const int fork_errno = errno;
+ HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) {
+ base::Thread thread("sandbox_tests");
+ BPF_ASSERT(thread.Start());
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedCloneFlagCrashes,
+ DEATH_MESSAGE(GetCloneErrorMessageContentForTests()),
+ BaselinePolicy) {
+ pid_t pid = syscall(__NR_clone, CLONE_THREAD | SIGCHLD);
+ HandlePostForkReturn(pid);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedKillCrashes,
+ DEATH_MESSAGE(GetKillErrorMessageContentForTests()),
+ BaselinePolicy) {
+ BPF_ASSERT_NE(1, getpid());
+ kill(1, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, CanKillSelf, BaselinePolicy) {
+ int sys_ret = kill(getpid(), 0);
+ BPF_ASSERT_EQ(0, sys_ret);
+}
+
+BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) {
+ int sv[2];
+ int sys_ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+
+ sys_ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+}
+
+// Not all architectures can restrict the domain for socketpair().
+#if defined(__x86_64__) || defined(__arm__)
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SocketpairWrongDomain,
+ DEATH_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ int sv[2];
+ ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv));
+ _exit(1);
+}
+#endif // defined(__x86_64__) || defined(__arm__)
+
+BPF_TEST_C(BaselinePolicy, EPERM_open, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = open("/proc/cpuinfo", O_RDONLY);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_access, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = access("/proc/cpuinfo", R_OK);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) {
+ errno = 0;
+ char buf[1024];
+ char* cwd = getcwd(buf, sizeof(buf));
+ BPF_ASSERT_EQ(NULL, cwd);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+// A failing test using this macro could be problematic since we perform
+// system calls by passing "0" as every argument.
+// The kernel could SIGSEGV the process or the system call itself could reboot
+// the machine. Some thoughts have been given when hand-picking the system
+// calls below to limit any potential side effects outside of the current
+// process.
+#define TEST_BASELINE_SIGSYS(sysno) \
+ BPF_DEATH_TEST_C(BaselinePolicy, \
+ SIGSYS_##sysno, \
+ DEATH_MESSAGE(GetErrorMessageContentForTests()), \
+ BaselinePolicy) { \
+ syscall(sysno, 0, 0, 0, 0, 0, 0); \
+ _exit(1); \
+ }
+
+TEST_BASELINE_SIGSYS(__NR_syslog);
+TEST_BASELINE_SIGSYS(__NR_sched_setaffinity);
+TEST_BASELINE_SIGSYS(__NR_timer_create);
+TEST_BASELINE_SIGSYS(__NR_io_cancel);
+TEST_BASELINE_SIGSYS(__NR_ptrace);
+TEST_BASELINE_SIGSYS(__NR_eventfd);
+TEST_BASELINE_SIGSYS(__NR_fgetxattr);
+TEST_BASELINE_SIGSYS(__NR_fanotify_init);
+TEST_BASELINE_SIGSYS(__NR_swapon);
+TEST_BASELINE_SIGSYS(__NR_chroot);
+TEST_BASELINE_SIGSYS(__NR_acct);
+TEST_BASELINE_SIGSYS(__NR_sysinfo);
+TEST_BASELINE_SIGSYS(__NR_inotify_init);
+TEST_BASELINE_SIGSYS(__NR_init_module);
+TEST_BASELINE_SIGSYS(__NR_keyctl);
+TEST_BASELINE_SIGSYS(__NR_mq_open);
+TEST_BASELINE_SIGSYS(__NR_vserver);
+TEST_BASELINE_SIGSYS(__NR_getcpu);
+TEST_BASELINE_SIGSYS(__NR_setpgid);
+TEST_BASELINE_SIGSYS(__NR_getitimer);
+
+#if !defined(OS_ANDROID)
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritence,
+ DEATH_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritencePrivate,
+ DEATH_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+#endif // !defined(OS_ANDROID)
+
+BPF_TEST_C(BaselinePolicy, PrctlDumpable, BaselinePolicy) {
+ const int is_dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+ BPF_ASSERT(is_dumpable == 1 || is_dumpable == 0);
+ const int prctl_ret = prctl(PR_SET_DUMPABLE, is_dumpable, 0, 0, 0, 0);
+ BPF_ASSERT_EQ(0, prctl_ret);
+}
+
+// Workaround incomplete Android headers.
+#if !defined(PR_CAPBSET_READ)
+#define PR_CAPBSET_READ 23
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ PrctlSigsys,
+ DEATH_MESSAGE(GetPrctlErrorMessageContentForTests()),
+ BaselinePolicy) {
+ prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
+ _exit(1);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
index 6ff71257526..57dc24e8f88 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -9,11 +9,17 @@
#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"
+#define SECCOMP_MESSAGE_COMMON_CONTENT "seccomp-bpf failure"
+#define SECCOMP_MESSAGE_CLONE_CONTENT "clone() failure"
+#define SECCOMP_MESSAGE_PRCTL_CONTENT "prctl() failure"
+#define SECCOMP_MESSAGE_IOCTL_CONTENT "ioctl() failure"
+#define SECCOMP_MESSAGE_KILL_CONTENT "(tg)kill() failure"
+#define SECCOMP_MESSAGE_FUTEX_CONTENT "futex() failure"
+
namespace {
inline bool IsArchitectureX86_64() {
@@ -55,7 +61,7 @@ void PrintSyscallError(uint32_t sysno) {
sysno_base10[i] = '0' + mod;
}
static const char kSeccompErrorPrefix[] =
- __FILE__":**CRASHING**:seccomp-bpf failure in syscall ";
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall ";
static const char kSeccompErrorPostfix[] = "\n";
WriteToStdErr(kSeccompErrorPrefix, sizeof(kSeccompErrorPrefix) - 1);
WriteToStdErr(sysno_base10, sizeof(sysno_base10));
@@ -95,11 +101,11 @@ intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {
// TODO(jln): refactor the reporting functions.
intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
+ static const char kSeccompCloneError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_CLONE_CONTENT "\n";
+ WriteToStdErr(kSeccompCloneError, sizeof(kSeccompCloneError) - 1);
// "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()) {
@@ -115,10 +121,10 @@ intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
void* /* aux */) {
+ static const char kSeccompPrctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_PRCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompPrctlError, sizeof(kSeccompPrctlError) - 1);
// 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);
@@ -129,10 +135,10 @@ intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
void* /* aux */) {
+ static const char kSeccompIoctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_IOCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompIoctlError, sizeof(kSeccompIoctlError) - 1);
// 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';
@@ -143,4 +149,56 @@ intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
_exit(1);
}
+intptr_t SIGSYSKillFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompKillError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_KILL_CONTENT "\n";
+ WriteToStdErr(kSeccompKillError, sizeof(kSeccompKillError) - 1);
+ // Make "request" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t pid = args.args[0];
+ volatile char* addr = reinterpret_cast<volatile char*>(pid & 0xFFF);
+ *addr = '\0';
+ // Hit the NULL page if this fails.
+ addr = reinterpret_cast<volatile char*>(pid & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompFutexError[] =
+ __FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_FUTEX_CONTENT "\n";
+ WriteToStdErr(kSeccompFutexError, sizeof(kSeccompFutexError) - 1);
+ volatile int futex_op = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(futex_op & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+const char* GetErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_COMMON_CONTENT;
+}
+
+const char* GetCloneErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_CLONE_CONTENT;
+}
+
+const char* GetPrctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_PRCTL_CONTENT;
+}
+
+const char* GetIoctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_IOCTL_CONTENT;
+}
+
+const char* GetKillErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_KILL_CONTENT;
+}
+
+const char* GetFutexErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_FUTEX_CONTENT;
+}
+
} // namespace sandbox.
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
index 3bf5c16db1f..280afa7a69f 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "build/build_config.h"
+#include "sandbox/sandbox_export.h"
// The handlers are suitable for use in Trap() error codes. They are
// guaranteed to be async-signal safe.
@@ -19,20 +20,41 @@ 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);
+SANDBOX_EXPORT intptr_t
+ CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux);
// The following three handlers are suitable to report failures with the
// clone(), prctl() and ioctl() system calls respectively.
// The 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);
+SANDBOX_EXPORT 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);
+SANDBOX_EXPORT 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);
+SANDBOX_EXPORT intptr_t
+ SIGSYSIoctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (pid & 0xFFF), where pid is the first
+// argument (and can be a tid).
+SANDBOX_EXPORT intptr_t
+ SIGSYSKillFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (op & 0xFFF), where op is the second
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
+
+// Following four functions return substrings of error messages used
+// in the above four functions. They are useful in death tests.
+SANDBOX_EXPORT const char* GetErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetCloneErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetPrctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetIoctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetKillErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetFutexErrorMessageContentForTests();
} // namespace sandbox.
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 9b417ce221f..16c37a06975 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -7,6 +7,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fcntl.h>
+#include <linux/futex.h>
#include <linux/net.h>
#include <sched.h>
#include <signal.h>
@@ -19,9 +20,12 @@
#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.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"
+#include "sandbox/linux/services/android_futex.h"
#if defined(OS_ANDROID)
#if !defined(F_DUPFD_CLOEXEC)
@@ -35,24 +39,24 @@
namespace {
-inline bool RunningOnASAN() {
-#if defined(ADDRESS_SANITIZER)
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
return true;
#else
return false;
#endif
}
-inline bool IsArchitectureX86_64() {
-#if defined(__x86_64__)
+inline bool IsArchitectureI386() {
+#if defined(__i386__)
return true;
#else
return false;
#endif
}
-inline bool IsArchitectureI386() {
-#if defined(__i386__)
+inline bool IsAndroid() {
+#if defined(OS_ANDROID)
return true;
#else
return false;
@@ -63,24 +67,40 @@ inline bool IsArchitectureI386() {
namespace sandbox {
+// Allow Glibc's and Android pthread creation flags, crash on any other
+// thread creation attempts and EPERM attempts to use neither
+// CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations.
ErrorCode RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox) {
- // Glibc's pthread.
- if (!RunningOnASAN()) {
+ if (!IsAndroid()) {
+ const uint64_t kGlibcPthreadFlags =
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID |
+ CLONE_CHILD_CLEARTID;
+
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,
+ kGlibcPthreadFlags,
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))));
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ CLONE_VM | CLONE_THREAD,
+ sandbox->Trap(SIGSYSCloneFailure, NULL),
+ ErrorCode(EPERM)));
} else {
- return ErrorCode(ErrorCode::ERR_ALLOWED);
+ const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const uint64_t kObsoleteAndroidCloneMask =
+ kAndroidCloneMask | CLONE_DETACHED;
+
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ kAndroidCloneMask,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ kObsoleteAndroidCloneMask,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ CLONE_VM | CLONE_THREAD,
+ sandbox->Trap(SIGSYSCloneFailure, NULL),
+ ErrorCode(EPERM))));
}
}
@@ -211,4 +231,46 @@ ErrorCode RestrictSocketcallCommand(SandboxBPF* sandbox) {
}
#endif
+ErrorCode RestrictKillTarget(pid_t target_pid, SandboxBPF* sandbox, int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill:
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ target_pid,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Trap(SIGSYSKillFailure, NULL));
+ case __NR_tkill:
+ return sandbox->Trap(SIGSYSKillFailure, NULL);
+ default:
+ NOTREACHED();
+ return sandbox->Trap(CrashSIGSYS_Handler, NULL);
+ }
+}
+
+ErrorCode RestrictFutex(SandboxBPF* sandbox) {
+ // In futex.c, the kernel does "int cmd = op & FUTEX_CMD_MASK;". We need to
+ // make sure that the combination below will cover every way to get
+ // FUTEX_CMP_REQUEUE_PI.
+ const int kBannedFutexBits =
+ ~(FUTEX_CMD_MASK | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME);
+ COMPILE_ASSERT(0 == kBannedFutexBits,
+ need_to_explicitly_blacklist_more_bits);
+
+ return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ FUTEX_CMP_REQUEUE_PI,
+ sandbox->Trap(SIGSYSFutexFailure, NULL),
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ FUTEX_CMP_REQUEUE_PI_PRIVATE,
+ sandbox->Trap(SIGSYSFutexFailure, NULL),
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ FUTEX_CMP_REQUEUE_PI | FUTEX_CLOCK_REALTIME,
+ sandbox->Trap(SIGSYSFutexFailure, NULL),
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ FUTEX_CMP_REQUEUE_PI_PRIVATE | FUTEX_CLOCK_REALTIME,
+ sandbox->Trap(SIGSYSFutexFailure, NULL),
+ ErrorCode(ErrorCode::ERR_ALLOWED)))));
+}
+
} // namespace sandbox.
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
index 65b7c472198..bc5a1c0e76a 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -5,11 +5,14 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+#include <unistd.h>
+
#include "build/build_config.h"
+#include "sandbox/sandbox_export.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.
+// SANDBOX_EXPORT ErrorCode suitable to restrict certain system call parameters.
namespace sandbox {
@@ -20,39 +23,48 @@ class SandboxBPF;
// Reject fork(2) attempts with EPERM.
// Don't restrict on ASAN.
// Crash if anything else is attempted.
-ErrorCode RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox);
+SANDBOX_EXPORT ErrorCode
+ RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox);
// Allow PR_SET_NAME, PR_SET_DUMPABLE, PR_GET_DUMPABLE.
// Crash if anything else is attempted.
-ErrorCode RestrictPrctl(SandboxBPF* sandbox);
+SANDBOX_EXPORT ErrorCode RestrictPrctl(SandboxBPF* sandbox);
// Allow TCGETS and FIONREAD.
// Crash if anything else is attempted.
-ErrorCode RestrictIoctl(SandboxBPF* sandbox);
+SANDBOX_EXPORT 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);
+SANDBOX_EXPORT ErrorCode RestrictMmapFlags(SandboxBPF* sandbox);
// Restrict the prot argument in mprotect(2).
// Only allow: PROT_READ | PROT_WRITE | PROT_EXEC.
-ErrorCode RestrictMprotectFlags(SandboxBPF* sandbox);
+SANDBOX_EXPORT 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);
+SANDBOX_EXPORT 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);
+SANDBOX_EXPORT ErrorCode RestrictSocketcallCommand(SandboxBPF* sandbox);
#endif
+// Restrict |sysno| (which must be kill, tkill or tgkill) by allowing tgkill or
+// kill iff the first parameter is |target_pid|, crashing otherwise or if
+// |sysno| is tkill.
+ErrorCode RestrictKillTarget(pid_t target_pid, SandboxBPF* sandbox, int sysno);
+
+// Crash if FUTEX_CMP_REQUEUE_PI is used in the second argument of futex(2).
+ErrorCode RestrictFutex(SandboxBPF* sandbox);
+
} // 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
index 032f6c3c472..e3db231502d 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -14,12 +14,11 @@ namespace sandbox {
// 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:
+ case __NR_tkill: // Deprecated.
return true;
default:
return false;
@@ -351,7 +350,6 @@ bool SyscallSets::IsKernelInternalApi(int sysno) {
// 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:
@@ -360,6 +358,7 @@ bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) {
case __NR_waitpid:
#endif
return true;
+ case __NR_clone: // Should be parameter-restricted.
case __NR_setns: // Privileged.
case __NR_fork:
#if defined(__i386__) || defined(__x86_64__)
@@ -375,12 +374,12 @@ bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) {
}
// It's difficult to restrict those, but there is attack surface here.
-bool SyscallSets::IsFutex(int sysno) {
+bool SyscallSets::IsAllowedFutex(int sysno) {
switch (sysno) {
- case __NR_futex:
case __NR_get_robust_list:
case __NR_set_robust_list:
return true;
+ case __NR_futex:
default:
return false;
}
@@ -547,14 +546,14 @@ bool SyscallSets::IsAllowedGeneralIo(int sysno) {
}
}
-bool SyscallSets::IsAllowedPrctl(int sysno) {
+bool SyscallSets::IsPrctl(int sysno) {
switch (sysno) {
- case __NR_prctl:
- return true;
- default:
#if defined(__x86_64__)
case __NR_arch_prctl:
#endif
+ case __NR_prctl:
+ return true;
+ default:
return false;
}
}
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
index d2cf1a17801..c1e412e9034 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "build/build_config.h"
+#include "sandbox/sandbox_export.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
@@ -15,7 +16,7 @@
namespace sandbox {
-class SyscallSets {
+class SANDBOX_EXPORT SyscallSets {
public:
static bool IsKill(int sysno);
static bool IsAllowedGettime(int sysno);
@@ -36,7 +37,7 @@ class SyscallSets {
// 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 IsAllowedFutex(int sysno);
static bool IsAllowedEpoll(int sysno);
static bool IsAllowedGetOrModifySocket(int sysno);
static bool IsDeniedGetOrModifySocket(int sysno);
@@ -52,7 +53,7 @@ class SyscallSets {
static bool IsAllowedAddressSpaceAccess(int sysno);
static bool IsAllowedGeneralIo(int sysno);
- static bool IsAllowedPrctl(int sysno);
+ static bool IsPrctl(int sysno);
static bool IsAllowedBasicScheduler(int sysno);
static bool IsAdminOperation(int sysno);
static bool IsKernelModule(int sysno);
diff --git a/chromium/sandbox/linux/seccomp-bpf/DEPS b/chromium/sandbox/linux/seccomp-bpf/DEPS
new file mode 100644
index 00000000000..15b2b36a3ef
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/services"
+]
diff --git a/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h b/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
new file mode 100644
index 00000000000..8890f798749
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
@@ -0,0 +1,65 @@
+// 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_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+// This templated class allows building a BPFTesterDelegate from a
+// deprecated-style BPF policy (that is a SyscallEvaluator function pointer,
+// instead of a SandboxBPFPolicy class), specified in |policy_function| and a
+// function pointer to a test in |test_function|.
+// This allows both the policy and the test function to take a pointer to an
+// object of type "Aux" as a parameter. This is used to implement the BPF_TEST
+// macro and should generally not be used directly.
+template <class Aux>
+class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
+ public:
+ typedef Aux AuxType;
+ BPFTesterCompatibilityDelegate(
+ void (*test_function)(AuxType*),
+ typename CompatibilityPolicy<AuxType>::SyscallEvaluator policy_function)
+ : aux_(),
+ test_function_(test_function),
+ policy_function_(policy_function) {}
+
+ virtual ~BPFTesterCompatibilityDelegate() {}
+
+ virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() OVERRIDE {
+ // The current method is guaranteed to only run in the child process
+ // running the test. In this process, the current object is guaranteed
+ // to live forever. So it's ok to pass aux_pointer_for_policy_ to
+ // the policy, which could in turn pass it to the kernel via Trap().
+ return scoped_ptr<SandboxBPFPolicy>(
+ new CompatibilityPolicy<AuxType>(policy_function_, &aux_));
+ }
+
+ virtual void RunTestFunction() OVERRIDE {
+ // Run the actual test.
+ // The current object is guaranteed to live forever in the child process
+ // where this will run.
+ test_function_(&aux_);
+ }
+
+ private:
+ AuxType aux_;
+ void (*test_function_)(AuxType*);
+ typename CompatibilityPolicy<AuxType>::SyscallEvaluator policy_function_;
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterCompatibilityDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
diff --git a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
index 7095c23b8c1..da92de80b79 100644
--- a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
+++ b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -5,110 +5,118 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
+#include "base/basictypes.h"
#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h"
#include "sandbox/linux/tests/unit_tests.h"
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
namespace sandbox {
-// A BPF_DEATH_TEST is just the same as a BPF_TEST, but it assumes that the
-// test will fail with a particular known error condition. Use the DEATH_XXX()
-// 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)
+// BPF_TEST_C() is a special version of SANDBOX_TEST(). It runs a test function
+// in a sub-process, under a seccomp-bpf policy specified in
+// |bpf_policy_class_name| without failing on configurations that are allowed
+// to not support seccomp-bpf in their kernels.
+// This is the preferred format for new BPF tests. |bpf_policy_class_name| is a
+// class name (which will be default-constructed) that implements the
+// SandboxBPFPolicy interface.
+// The test function's body can simply follow. Test functions should use
+// the BPF_ASSERT macros defined below, not GTEST's macros. The use of
+// CHECK* macros is supported but less robust.
+#define BPF_TEST_C(test_case_name, test_name, bpf_policy_class_name) \
+ BPF_DEATH_TEST_C( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_policy_class_name)
+
+// Identical to BPF_TEST_C but allows to specify the nature of death.
+#define BPF_DEATH_TEST_C( \
+ test_case_name, test_name, death, bpf_policy_class_name) \
+ void BPF_TEST_C_##test_name(); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterSimpleDelegate<bpf_policy_class_name>( \
+ BPF_TEST_C_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_C_##test_name()
+
+// This form of BPF_TEST is a little verbose and should be reserved for complex
+// tests where a lot of control is required.
+// |bpf_tester_delegate_class| must be a classname implementing the
+// BPFTesterDelegate interface.
+#define BPF_TEST_D(test_case_name, test_name, bpf_tester_delegate_class) \
+ BPF_DEATH_TEST_D( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_tester_delegate_class)
+
+// Identical to BPF_TEST_D but allows to specify the nature of death.
+#define BPF_DEATH_TEST_D( \
+ test_case_name, test_name, death, bpf_tester_delegate_class) \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new bpf_tester_delegate_class()); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ }
-// 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.
-// Also, it takes advantage of the Die class to avoid calling LOG(FATAL), from
-// inside our tests, as we don't need or even want all the error handling that
-// LOG(FATAL) would do.
+// Assertions are handled exactly the same as with a normal SANDBOX_TEST()
+#define BPF_ASSERT SANDBOX_ASSERT
+#define BPF_ASSERT_EQ(x, y) BPF_ASSERT((x) == (y))
+#define BPF_ASSERT_NE(x, y) BPF_ASSERT((x) != (y))
+#define BPF_ASSERT_LT(x, y) BPF_ASSERT((x) < (y))
+#define BPF_ASSERT_GT(x, y) BPF_ASSERT((x) > (y))
+#define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y))
+#define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y))
+
+// This form of BPF_TEST is now discouraged (but still allowed) in favor of
+// BPF_TEST_D and BPF_TEST_C.
+// The |policy| parameter should be a SyscallEvaluator function pointer
+// (which is now a deprecated way of expressing policies).
// 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 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...) \
+// communicate data between the BPF_TEST() and a Trap() function. The life-time
+// of this object is the same as the life-time of the process running under the
+// seccomp-bpf policy.
+// The type specified in |aux| and the last parameter of the policy function
+// must be compatible. |aux| must not be void.
+#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 {
+// A BPF_DEATH_TEST is just the same as a BPF_TEST, but it assumes that the
+// test will fail with a particular known error condition. Use the DEATH_XXX()
+// macros from unit_tests.h to specify the expected error condition.
+#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux) \
+ void BPF_TEST_##test_name( \
+ sandbox::BPFTesterCompatibilityDelegate<aux>::AuxType* BPF_AUX); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterCompatibilityDelegate<aux>(BPF_TEST_##test_name, \
+ policy)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_##test_name( \
+ sandbox::BPFTesterCompatibilityDelegate<aux>::AuxType* BPF_AUX)
+
+// This class takes a simple function pointer as a constructor parameter and a
+// class name as a template parameter to implement the BPFTesterDelegate
+// interface which can be used to build BPF unittests with
+// the SandboxBPFTestRunner class.
+template <class PolicyClass>
+class BPFTesterSimpleDelegate : public BPFTesterDelegate {
public:
- typedef Aux AuxType;
-
- class TestArgs {
- public:
- TestArgs(void (*t)(AuxType&), sandbox::SandboxBPF::EvaluateSyscall p)
- : test_(t), policy_(p), aux_() {}
-
- void (*test() const)(AuxType&) { return test_; }
- sandbox::SandboxBPF::EvaluateSyscall policy() const { return policy_; }
+ explicit BPFTesterSimpleDelegate(void (*test_function)(void))
+ : test_function_(test_function) {}
+ virtual ~BPFTesterSimpleDelegate() {}
- private:
- friend class BPFTests;
-
- void (*test_)(AuxType&);
- sandbox::SandboxBPF::EvaluateSyscall policy_;
- AuxType aux_;
- };
-
- 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(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) ==
- sandbox::SandboxBPF::STATUS_AVAILABLE);
-
- // Initialize and then start the sandbox with our custom policy
- sandbox::SandboxBPF sandbox;
- sandbox.set_proc_fd(proc_fd);
- sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_);
- sandbox.SandboxBPF::StartSandbox();
-
- arg->test()(arg->aux_);
- } else {
- printf("This BPF test is not fully running in this configuration!\n");
- // Android, ARM and Valgrind are the three only configurations where we
- // accept not having kernel BPF support.
- // TODO(jln): remote ARM from this list when possible (crbug.com/243478).
- if (!IsAndroid() && !IsRunningOnValgrind() && !IsArchitectureArm()) {
- const bool seccomp_bpf_is_supported = false;
- BPF_ASSERT(seccomp_bpf_is_supported);
- }
- // Call the compiler and verify the policy. That's the least we can do,
- // if we don't have kernel support.
- sandbox::SandboxBPF sandbox;
- sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_);
- sandbox::SandboxBPF::Program* program =
- sandbox.AssembleFilter(true /* force_verification */);
- delete program;
- sandbox::UnitTests::IgnoreThisTest();
- }
+ virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() OVERRIDE {
+ return scoped_ptr<SandboxBPFPolicy>(new PolicyClass());
+ }
+ virtual void RunTestFunction() OVERRIDE {
+ DCHECK(test_function_);
+ test_function_();
}
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(BPFTests);
+ void (*test_function_)(void);
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterSimpleDelegate);
};
} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
new file mode 100644
index 00000000000..bd18412bee6
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -0,0 +1,139 @@
+// 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/seccomp-bpf/bpf_tests.h"
+
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+class FourtyTwo {
+ public:
+ static const int kMagicValue = 42;
+ FourtyTwo() : value_(kMagicValue) {}
+ int value() { return value_; }
+
+ private:
+ int value_;
+ DISALLOW_COPY_AND_ASSIGN(FourtyTwo);
+};
+
+ErrorCode EmptyPolicyTakesClass(SandboxBPF* sandbox,
+ int sysno,
+ FourtyTwo* fourty_two) {
+ // |aux| should point to an instance of FourtyTwo.
+ BPF_ASSERT(fourty_two);
+ BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
+ if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(BPFTest,
+ BPFAUXPointsToClass,
+ EmptyPolicyTakesClass,
+ FourtyTwo /* *BPF_AUX */) {
+ // BPF_AUX should point to an instance of FourtyTwo.
+ BPF_ASSERT(BPF_AUX);
+ BPF_ASSERT(FourtyTwo::kMagicValue == BPF_AUX->value());
+}
+
+void DummyTestFunction(FourtyTwo *fourty_two) {
+}
+
+TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) {
+ // Don't do anything, simply gives dynamic tools an opportunity to detect
+ // leaks.
+ {
+ BPFTesterCompatibilityDelegate<FourtyTwo> simple_delegate(
+ DummyTestFunction, EmptyPolicyTakesClass);
+ }
+ {
+ // Test polymorphism.
+ scoped_ptr<BPFTesterDelegate> simple_delegate(
+ new BPFTesterCompatibilityDelegate<FourtyTwo>(DummyTestFunction,
+ EmptyPolicyTakesClass));
+ }
+}
+
+class EnosysPtracePolicy : public SandboxBPFPolicy {
+ public:
+ EnosysPtracePolicy() {
+ my_pid_ = syscall(__NR_getpid);
+ }
+ virtual ~EnosysPtracePolicy() {
+ // Policies should be able to bind with the process on which they are
+ // created. They should never be created in a parent process.
+ BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid));
+ }
+
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
+ int system_call_number) const OVERRIDE {
+ if (!SandboxBPF::IsValidSyscallNumber(system_call_number)) {
+ return ErrorCode(ENOSYS);
+ } else if (system_call_number == __NR_ptrace) {
+ // The EvaluateSyscall function should run in the process that created
+ // the current object.
+ BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid));
+ return ErrorCode(ENOSYS);
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+ }
+
+ private:
+ pid_t my_pid_;
+ DISALLOW_COPY_AND_ASSIGN(EnosysPtracePolicy);
+};
+
+class BasicBPFTesterDelegate : public BPFTesterDelegate {
+ public:
+ BasicBPFTesterDelegate() {}
+ virtual ~BasicBPFTesterDelegate() {}
+
+ virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() OVERRIDE {
+ return scoped_ptr<SandboxBPFPolicy>(new EnosysPtracePolicy());
+ }
+ virtual void RunTestFunction() OVERRIDE {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicBPFTesterDelegate);
+};
+
+// This is the most powerful and complex way to create a BPF test, but it
+// requires a full class definition (BasicBPFTesterDelegate).
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, BasicBPFTesterDelegate);
+
+// This is the simplest form of BPF tests.
+BPF_TEST_C(BPFTest, BPFTestWithInlineTest, EnosysPtracePolicy) {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.cc b/chromium/sandbox/linux/seccomp-bpf/codegen.cc
index 8fb1701179e..c90bffcad30 100644
--- a/chromium/sandbox/linux/seccomp-bpf/codegen.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/codegen.cc
@@ -4,6 +4,7 @@
#include <stdio.h>
+#include "base/logging.h"
#include "sandbox/linux/seccomp-bpf/codegen.h"
namespace {
@@ -105,6 +106,8 @@ void CodeGen::PrintProgram(const SandboxBPF::Program& program) {
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_ACTION) == SECCOMP_RET_TRACE) {
+ fprintf(stderr, "Trace #%d\n", iter->k & SECCOMP_RET_DATA);
} else if (iter->k == SECCOMP_RET_ALLOW) {
fprintf(stderr, "Allowed\n");
} else {
@@ -432,67 +435,84 @@ static int PointerCompare(const BasicBlock* block1,
// We compare the sequence of instructions in both basic blocks.
const Instructions& insns1 = block1->instructions;
const Instructions& insns2 = block2->instructions;
+ // Basic blocks should never be empty.
+ CHECK(!insns1.empty());
+ CHECK(!insns2.empty());
+
Instructions::const_iterator iter1 = insns1.begin();
Instructions::const_iterator iter2 = insns2.begin();
for (;; ++iter1, ++iter2) {
// If we have reached the end of the sequence of instructions in one or
// both basic blocks, we know the relative ordering between the two blocks
// and can return.
- if (iter1 == insns1.end()) {
- return iter2 == insns2.end() ? 0 : -1;
- } else if (iter2 == insns2.end()) {
- return 1;
+ if (iter1 == insns1.end() || iter2 == insns2.end()) {
+ if (iter1 != insns1.end()) {
+ return 1;
+ }
+ if (iter2 != insns2.end()) {
+ return -1;
+ }
+
+ // If the two blocks are the same length (and have elementwise-equal code
+ // and k fields) and their last instructions are neither a JMP nor a RET
+ // (which is the only way we can reach this point), then we must compare
+ // their successors.
+ Instruction* const insns1_last = insns1.back();
+ Instruction* const insns2_last = insns2.back();
+ CHECK(BPF_CLASS(insns1_last->code) != BPF_JMP &&
+ BPF_CLASS(insns1_last->code) != BPF_RET);
+
+ // Non jumping instructions will always have a valid next instruction.
+ CHECK(insns1_last->next);
+ CHECK(insns2_last->next);
+ return PointerCompare(blocks.find(insns1_last->next)->second,
+ blocks.find(insns2_last->next)->second,
+ blocks);
}
// Compare the individual fields for both instructions.
const Instruction& insn1 = **iter1;
const Instruction& insn2 = **iter2;
- if (insn1.code == insn2.code) {
- if (insn1.k == insn2.k) {
- // Only conditional jump instructions use the jt_ptr and jf_ptr
- // fields.
- if (BPF_CLASS(insn1.code) == BPF_JMP) {
- if (BPF_OP(insn1.code) != BPF_JA) {
- // Recursively compare the "true" and "false" branches.
- // A well-formed BPF program can't have any cycles, so we know
- // that our recursive algorithm will ultimately terminate.
- // In the unlikely event that the programmer made a mistake and
- // went out of the way to give us a cyclic program, we will crash
- // with a stack overflow. We are OK with that.
- int c = PointerCompare(blocks.find(insn1.jt_ptr)->second,
- blocks.find(insn2.jt_ptr)->second,
- blocks);
- if (c == 0) {
- c = PointerCompare(blocks.find(insn1.jf_ptr)->second,
- blocks.find(insn2.jf_ptr)->second,
- blocks);
- if (c == 0) {
- continue;
- } else {
- return c;
- }
- } else {
- return c;
- }
- } else {
- int c = PointerCompare(blocks.find(insn1.jt_ptr)->second,
- blocks.find(insn2.jt_ptr)->second,
- blocks);
- if (c == 0) {
- continue;
- } else {
- return c;
- }
- }
- } else {
- continue;
- }
- } else {
- return insn1.k - insn2.k;
- }
- } else {
+ if (insn1.code != insn2.code) {
return insn1.code - insn2.code;
}
+ if (insn1.k != insn2.k) {
+ return insn1.k - insn2.k;
+ }
+
+ // Sanity check: If we're looking at a JMP or RET instruction, by definition
+ // it should be the last instruction of the basic block.
+ if (BPF_CLASS(insn1.code) == BPF_JMP || BPF_CLASS(insn1.code) == BPF_RET) {
+ CHECK_EQ(insns1.back(), &insn1);
+ CHECK_EQ(insns2.back(), &insn2);
+ }
+
+ // RET instructions terminate execution, and only JMP instructions use the
+ // jt_ptr and jf_ptr fields. Anything else can continue to the next
+ // instruction in the basic block.
+ if (BPF_CLASS(insn1.code) == BPF_RET) {
+ return 0;
+ } else if (BPF_CLASS(insn1.code) != BPF_JMP) {
+ continue;
+ }
+
+ // Recursively compare the "true" and "false" branches.
+ // A well-formed BPF program can't have any cycles, so we know
+ // that our recursive algorithm will ultimately terminate.
+ // In the unlikely event that the programmer made a mistake and
+ // went out of the way to give us a cyclic program, we will crash
+ // with a stack overflow. We are OK with that.
+ if (BPF_OP(insn1.code) != BPF_JA) {
+ int c = PointerCompare(blocks.find(insn1.jf_ptr)->second,
+ blocks.find(insn2.jf_ptr)->second,
+ blocks);
+ if (c != 0) {
+ return c;
+ }
+ }
+ return PointerCompare(blocks.find(insn1.jt_ptr)->second,
+ blocks.find(insn2.jt_ptr)->second,
+ blocks);
}
}
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.h b/chromium/sandbox/linux/seccomp-bpf/codegen.h
index 2745e51194e..1c4cd46a5a9 100644
--- a/chromium/sandbox/linux/seccomp-bpf/codegen.h
+++ b/chromium/sandbox/linux/seccomp-bpf/codegen.h
@@ -12,6 +12,7 @@
#include "sandbox/linux/seccomp-bpf/basicblock.h"
#include "sandbox/linux/seccomp-bpf/instruction.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -52,7 +53,7 @@ typedef std::map<const BasicBlock*, int> IncomingBranches;
// static_cast<unsigned short>(program->size()), &program[0] };
// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
//
-class CodeGen {
+class SANDBOX_EXPORT CodeGen {
public:
CodeGen();
~CodeGen();
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc
index 0539a0d4337..52fc24c6927 100644
--- a/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc
@@ -24,47 +24,44 @@ class SandboxUnittestHelper : public SandboxBPF {
class CodeGenUnittestHelper : public CodeGen {
public:
void FindBranchTargets(const Instruction& instructions,
- BranchTargets *branch_targets) {
+ BranchTargets* branch_targets) {
CodeGen::FindBranchTargets(instructions, branch_targets);
}
- BasicBlock *CutGraphIntoBasicBlocks(Instruction *insns,
+ BasicBlock* CutGraphIntoBasicBlocks(Instruction* insns,
const BranchTargets& branch_targets,
- TargetsToBlocks *blocks) {
+ TargetsToBlocks* blocks) {
return CodeGen::CutGraphIntoBasicBlocks(insns, branch_targets, blocks);
}
- void MergeTails(TargetsToBlocks *blocks) {
- CodeGen::MergeTails(blocks);
- }
+ void MergeTails(TargetsToBlocks* blocks) { CodeGen::MergeTails(blocks); }
};
-enum { NO_FLAGS = 0x0000,
- HAS_MERGEABLE_TAILS = 0x0001,
-};
+enum { NO_FLAGS = 0x0000, HAS_MERGEABLE_TAILS = 0x0001, };
-Instruction *SampleProgramOneInstruction(CodeGen *codegen, int *flags) {
+Instruction* SampleProgramOneInstruction(CodeGen* codegen, int* flags) {
// Create the most basic valid BPF program:
// RET ERR_ALLOWED
*flags = NO_FLAGS;
- return codegen->MakeInstruction(BPF_RET+BPF_K,
+ return codegen->MakeInstruction(BPF_RET + BPF_K,
ErrorCode(ErrorCode::ERR_ALLOWED));
}
-Instruction *SampleProgramSimpleBranch(CodeGen *codegen, int *flags) {
+Instruction* SampleProgramSimpleBranch(CodeGen* codegen, int* flags) {
// Create a program with a single branch:
// JUMP if eq 42 then $0 else $1
// 0: RET EPERM
// 1: RET ERR_ALLOWED
*flags = NO_FLAGS;
- return codegen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 42,
- codegen->MakeInstruction(BPF_RET+BPF_K,
- ErrorCode(EPERM)),
- codegen->MakeInstruction(BPF_RET+BPF_K,
- ErrorCode(ErrorCode::ERR_ALLOWED)));
+ return codegen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 42,
+ codegen->MakeInstruction(BPF_RET + BPF_K, ErrorCode(EPERM)),
+ codegen->MakeInstruction(BPF_RET + BPF_K,
+ ErrorCode(ErrorCode::ERR_ALLOWED)));
}
-Instruction *SampleProgramAtypicalBranch(CodeGen *codegen, int *flags) {
+Instruction* SampleProgramAtypicalBranch(CodeGen* codegen, int* flags) {
// Create a program with a single branch:
// JUMP if eq 42 then $0 else $0
// 0: RET ERR_ALLOWED
@@ -74,13 +71,12 @@ Instruction *SampleProgramAtypicalBranch(CodeGen *codegen, int *flags) {
// This needs to be reflected in our choice of "flags".
*flags = NO_FLAGS;
- Instruction *ret =
- codegen->MakeInstruction(BPF_RET+BPF_K,
- ErrorCode(ErrorCode::ERR_ALLOWED));
- return codegen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 42, ret, ret);
+ Instruction* ret = codegen->MakeInstruction(
+ BPF_RET + BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED));
+ return codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
}
-Instruction *SampleProgramComplex(CodeGen *codegen, int *flags) {
+Instruction* SampleProgramComplex(CodeGen* codegen, int* flags) {
// Creates a basic BPF program that we'll use to test some of the code:
// JUMP if eq 42 the $0 else $1 (insn6)
// 0: LD 23 (insn5)
@@ -92,31 +88,33 @@ Instruction *SampleProgramComplex(CodeGen *codegen, int *flags) {
// RET ErrorCode(42) (insn3+)
*flags = HAS_MERGEABLE_TAILS;
- Instruction *insn0 = codegen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, 42);
+ Instruction* insn0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42);
SANDBOX_ASSERT(insn0);
- SANDBOX_ASSERT(insn0->code == BPF_LD+BPF_W+BPF_ABS);
+ SANDBOX_ASSERT(insn0->code == BPF_LD + BPF_W + BPF_ABS);
SANDBOX_ASSERT(insn0->k == 42);
SANDBOX_ASSERT(insn0->next == NULL);
- Instruction *insn1 = codegen->MakeInstruction(BPF_JMP+BPF_JA, 0, insn0);
+ Instruction* insn1 = codegen->MakeInstruction(BPF_JMP + BPF_JA, 0, insn0);
SANDBOX_ASSERT(insn1);
- SANDBOX_ASSERT(insn1->code == BPF_JMP+BPF_JA);
+ SANDBOX_ASSERT(insn1->code == BPF_JMP + BPF_JA);
SANDBOX_ASSERT(insn1->jt_ptr == insn0);
- Instruction *insn2 = codegen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(42));
+ Instruction* insn2 = codegen->MakeInstruction(BPF_RET + BPF_K, ErrorCode(42));
SANDBOX_ASSERT(insn2);
- SANDBOX_ASSERT(insn2->code == BPF_RET+BPF_K);
+ SANDBOX_ASSERT(insn2->code == BPF_RET + BPF_K);
SANDBOX_ASSERT(insn2->next == NULL);
// We explicitly duplicate instructions so that MergeTails() can coalesce
// them later.
- Instruction *insn3 = codegen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, 42,
- codegen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(42)));
+ Instruction* insn3 = codegen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ 42,
+ codegen->MakeInstruction(BPF_RET + BPF_K, ErrorCode(42)));
- Instruction *insn4 = codegen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 42,
- insn1, insn3);
+ Instruction* insn4 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn1, insn3);
SANDBOX_ASSERT(insn4);
- SANDBOX_ASSERT(insn4->code == BPF_JMP+BPF_JEQ+BPF_K);
+ SANDBOX_ASSERT(insn4->code == BPF_JMP + BPF_JEQ + BPF_K);
SANDBOX_ASSERT(insn4->k == 42);
SANDBOX_ASSERT(insn4->jt_ptr == insn1);
SANDBOX_ASSERT(insn4->jf_ptr == insn3);
@@ -124,10 +122,10 @@ Instruction *SampleProgramComplex(CodeGen *codegen, int *flags) {
codegen->JoinInstructions(insn0, insn2);
SANDBOX_ASSERT(insn0->next == insn2);
- Instruction *insn5 = codegen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
- 23, insn4);
+ Instruction* insn5 =
+ codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
SANDBOX_ASSERT(insn5);
- SANDBOX_ASSERT(insn5->code == BPF_LD+BPF_W+BPF_ABS);
+ SANDBOX_ASSERT(insn5->code == BPF_LD + BPF_W + BPF_ABS);
SANDBOX_ASSERT(insn5->k == 23);
SANDBOX_ASSERT(insn5->next == insn4);
@@ -137,18 +135,113 @@ Instruction *SampleProgramComplex(CodeGen *codegen, int *flags) {
// This also gives us a diamond-shaped pattern in our graph, which stresses
// another aspect of the topo-sort algorithm (namely, the ability to
// correctly count the incoming branches for subtrees that are not disjunct).
- Instruction *insn6 = codegen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 42,
- insn5, insn4);
+ Instruction* insn6 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
return insn6;
}
-void ForAllPrograms(void (*test)(CodeGenUnittestHelper *, Instruction *, int)){
- Instruction *(*function_table[])(CodeGen *codegen, int *flags) = {
+Instruction* SampleProgramConfusingTails(CodeGen* codegen, int* flags) {
+ // This simple program demonstrates https://crbug.com/351103/
+ // The two "LOAD 0" instructions are blocks of their own. MergeTails() could
+ // be tempted to merge them since they are the same. However, they are
+ // not mergeable because they fall-through to non semantically equivalent
+ // blocks.
+ // Without the fix for this bug, this program should trigger the check in
+ // CompileAndCompare: the serialized graphs from the program and its compiled
+ // version will differ.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0x50000 // errno = 0
+ // 7) RET 0x50001 // errno = 1
+ *flags = NO_FLAGS;
+
+ Instruction* i7 = codegen->MakeInstruction(BPF_RET, ErrorCode(1));
+ Instruction* i6 = codegen->MakeInstruction(BPF_RET, ErrorCode(0));
+ Instruction* i5 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ Instruction* i3 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ Instruction* i1 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ return i0;
+}
+
+Instruction* SampleProgramConfusingTailsBasic(CodeGen* codegen, int* flags) {
+ // Without the fix for https://crbug.com/351103/, (see
+ // SampleProgramConfusingTails()), this would generate a cyclic graph and
+ // crash as the two "LOAD 0" instructions would get merged.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) RET 0x50001 // errno = 1
+ *flags = NO_FLAGS;
+
+ Instruction* i5 = codegen->MakeInstruction(BPF_RET, ErrorCode(1));
+ Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ Instruction* i3 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ Instruction* i1 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ return i0;
+}
+
+Instruction* SampleProgramConfusingTailsMergeable(CodeGen* codegen,
+ int* flags) {
+ // This is similar to SampleProgramConfusingTails(), except that
+ // instructions 2 and 4 are now RET instructions.
+ // In PointerCompare(), this exercises the path where two blocks are of the
+ // same length and identical and the last instruction is a JMP or RET, so the
+ // following blocks don't need to be looked at and the blocks are mergeable.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) RET 0x5002a // errno = 42
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) RET 0x5002a // errno = 42
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0x50000 // errno = 0
+ // 7) RET 0x50001 // errno = 1
+ *flags = HAS_MERGEABLE_TAILS;
+
+ Instruction* i7 = codegen->MakeInstruction(BPF_RET, ErrorCode(1));
+ Instruction* i6 = codegen->MakeInstruction(BPF_RET, ErrorCode(0));
+ Instruction* i5 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ Instruction* i4 = codegen->MakeInstruction(BPF_RET, ErrorCode(42));
+ Instruction* i3 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ Instruction* i2 = codegen->MakeInstruction(BPF_RET, ErrorCode(42));
+ Instruction* i1 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ return i0;
+}
+void ForAllPrograms(void (*test)(CodeGenUnittestHelper*, Instruction*, int)) {
+ Instruction* (*function_table[])(CodeGen* codegen, int* flags) = {
SampleProgramOneInstruction,
SampleProgramSimpleBranch,
SampleProgramAtypicalBranch,
SampleProgramComplex,
+ SampleProgramConfusingTails,
+ SampleProgramConfusingTailsBasic,
+ SampleProgramConfusingTailsMergeable,
};
for (size_t i = 0; i < arraysize(function_table); ++i) {
@@ -159,8 +252,8 @@ void ForAllPrograms(void (*test)(CodeGenUnittestHelper *, Instruction *, int)){
}
}
-void MakeInstruction(CodeGenUnittestHelper *codegen,
- Instruction *program, int) {
+void MakeInstruction(CodeGenUnittestHelper* codegen,
+ Instruction* program, int) {
// Nothing to do here
}
@@ -168,7 +261,7 @@ SANDBOX_TEST(CodeGen, MakeInstruction) {
ForAllPrograms(MakeInstruction);
}
-void FindBranchTargets(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
+void FindBranchTargets(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
BranchTargets branch_targets;
codegen->FindBranchTargets(*prg, &branch_targets);
@@ -178,11 +271,11 @@ void FindBranchTargets(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
// targets of BPF_JMP instructions are represented in the "branch_targets".
// At the same time, compute a set of both the branch targets and all the
// instructions in the program.
- std::vector<Instruction *> stack;
- std::set<Instruction *> all_instructions;
- std::set<Instruction *> target_instructions;
+ std::vector<Instruction*> stack;
+ std::set<Instruction*> all_instructions;
+ std::set<Instruction*> target_instructions;
BranchTargets::const_iterator end = branch_targets.end();
- for (Instruction *insn = prg;;) {
+ for (Instruction* insn = prg;;) {
all_instructions.insert(insn);
if (BPF_CLASS(insn->code) == BPF_JMP) {
target_instructions.insert(insn->jt_ptr);
@@ -215,8 +308,10 @@ void FindBranchTargets(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
// "branch_targets" that FindBranchTargets() computed for us.
Instructions non_target_instructions(all_instructions.size() -
target_instructions.size());
- set_difference(all_instructions.begin(), all_instructions.end(),
- target_instructions.begin(), target_instructions.end(),
+ set_difference(all_instructions.begin(),
+ all_instructions.end(),
+ target_instructions.begin(),
+ target_instructions.end(),
non_target_instructions.begin());
for (Instructions::const_iterator iter = non_target_instructions.begin();
iter != non_target_instructions.end();
@@ -225,20 +320,19 @@ void FindBranchTargets(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
}
}
-SANDBOX_TEST(CodeGen, FindBranchTargets) {
- ForAllPrograms(FindBranchTargets);
-}
+SANDBOX_TEST(CodeGen, FindBranchTargets) { ForAllPrograms(FindBranchTargets); }
-void CutGraphIntoBasicBlocks(CodeGenUnittestHelper *codegen,
- Instruction *prg, int) {
+void CutGraphIntoBasicBlocks(CodeGenUnittestHelper* codegen,
+ Instruction* prg,
+ int) {
BranchTargets branch_targets;
codegen->FindBranchTargets(*prg, &branch_targets);
TargetsToBlocks all_blocks;
- BasicBlock *first_block =
- codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
+ BasicBlock* first_block =
+ codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
SANDBOX_ASSERT(first_block != NULL);
SANDBOX_ASSERT(first_block->instructions.size() > 0);
- Instruction *first_insn = first_block->instructions[0];
+ Instruction* first_insn = first_block->instructions[0];
// Basic blocks are supposed to start with a branch target and end with
// either a jump or a return instruction. It can also end, if the next
@@ -247,13 +341,13 @@ void CutGraphIntoBasicBlocks(CodeGenUnittestHelper *codegen,
for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin();
bb_iter != all_blocks.end();
++bb_iter) {
- BasicBlock *bb = bb_iter->second;
+ BasicBlock* bb = bb_iter->second;
SANDBOX_ASSERT(bb != NULL);
SANDBOX_ASSERT(bb->instructions.size() > 0);
- Instruction *insn = bb->instructions[0];
+ Instruction* insn = bb->instructions[0];
SANDBOX_ASSERT(insn == first_insn ||
branch_targets.find(insn) != branch_targets.end());
- for (Instructions::const_iterator insn_iter = bb->instructions.begin();;){
+ for (Instructions::const_iterator insn_iter = bb->instructions.begin();;) {
insn = *insn_iter;
if (++insn_iter != bb->instructions.end()) {
SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_JMP);
@@ -261,8 +355,7 @@ void CutGraphIntoBasicBlocks(CodeGenUnittestHelper *codegen,
} else {
SANDBOX_ASSERT(BPF_CLASS(insn->code) == BPF_JMP ||
BPF_CLASS(insn->code) == BPF_RET ||
- branch_targets.find(insn->next) !=
- branch_targets.end());
+ branch_targets.find(insn->next) != branch_targets.end());
break;
}
SANDBOX_ASSERT(branch_targets.find(*insn_iter) == branch_targets.end());
@@ -274,13 +367,12 @@ SANDBOX_TEST(CodeGen, CutGraphIntoBasicBlocks) {
ForAllPrograms(CutGraphIntoBasicBlocks);
}
-void MergeTails(CodeGenUnittestHelper *codegen, Instruction *prg,
- int flags) {
+void MergeTails(CodeGenUnittestHelper* codegen, Instruction* prg, int flags) {
BranchTargets branch_targets;
codegen->FindBranchTargets(*prg, &branch_targets);
TargetsToBlocks all_blocks;
- BasicBlock *first_block =
- codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
+ BasicBlock* first_block =
+ codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
// The shape of our graph and thus the function of our program should
// still be unchanged after we run MergeTails(). We verify this by
@@ -294,8 +386,8 @@ void MergeTails(CodeGenUnittestHelper *codegen, Instruction *prg,
// our graph.
for (int i = 0;;) {
// Traverse the entire program in depth-first order.
- std::vector<BasicBlock *> stack;
- for (BasicBlock *bb = first_block;;) {
+ std::vector<BasicBlock*> stack;
+ for (BasicBlock* bb = first_block;;) {
// Serialize the instructions in this basic block. In general, we only
// need to serialize "code" and "k"; except for a BPF_JA instruction
// where "k" isn't set.
@@ -303,23 +395,23 @@ void MergeTails(CodeGenUnittestHelper *codegen, Instruction *prg,
for (Instructions::const_iterator iter = bb->instructions.begin();
iter != bb->instructions.end();
++iter) {
- graph[i].append(reinterpret_cast<char *>(&(*iter)->code),
+ graph[i].append(reinterpret_cast<char*>(&(*iter)->code),
sizeof((*iter)->code));
if (BPF_CLASS((*iter)->code) != BPF_JMP ||
BPF_OP((*iter)->code) != BPF_JA) {
- graph[i].append(reinterpret_cast<char *>(&(*iter)->k),
+ graph[i].append(reinterpret_cast<char*>(&(*iter)->k),
sizeof((*iter)->k));
}
}
// Also serialize the addresses the basic blocks as we encounter them.
// This will change as basic blocks are coalesed by MergeTails().
- edges[i].append(reinterpret_cast<char *>(&bb), sizeof(bb));
+ edges[i].append(reinterpret_cast<char*>(&bb), sizeof(bb));
// Depth-first traversal of the graph. We only ever need to look at the
// very last instruction in the basic block, as that is the only one that
// can change code flow.
- Instruction *insn = bb->instructions.back();
+ Instruction* insn = bb->instructions.back();
if (BPF_CLASS(insn->code) == BPF_JMP) {
// For jump instructions, we need to remember the "false" branch while
// traversing the "true" branch. This is not necessary for BPF_JA which
@@ -359,7 +451,7 @@ SANDBOX_TEST(CodeGen, MergeTails) {
ForAllPrograms(MergeTails);
}
-void CompileAndCompare(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
+void CompileAndCompare(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
// TopoSortBasicBlocks() has internal checks that cause it to fail, if it
// detects a problem. Typically, if anything goes wrong, this looks to the
// TopoSort algorithm as if there had been cycles in the input data.
@@ -375,7 +467,7 @@ void CompileAndCompare(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
// before calling Compile().
std::string source;
Instructions source_stack;
- for (const Instruction *insn = prg, *next; insn; insn = next) {
+ for (const Instruction* insn = prg, *next; insn; insn = next) {
if (BPF_CLASS(insn->code) == BPF_JMP) {
if (BPF_OP(insn->code) == BPF_JA) {
// Do not serialize BPF_JA instructions (see above).
@@ -398,10 +490,9 @@ void CompileAndCompare(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
// Only serialize "code" and "k". That's all the information we need to
// compare. The rest of the information is encoded in the order of
// instructions.
- source.append(reinterpret_cast<const char *>(&insn->code),
+ source.append(reinterpret_cast<const char*>(&insn->code),
sizeof(insn->code));
- source.append(reinterpret_cast<const char *>(&insn->k),
- sizeof(insn->k));
+ source.append(reinterpret_cast<const char*>(&insn->k), sizeof(insn->k));
}
// Compile the program
@@ -434,8 +525,8 @@ void CompileAndCompare(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
++idx;
}
// Serialize the same information that we serialized before compilation.
- assembly.append(reinterpret_cast<char *>(&insn.code), sizeof(insn.code));
- assembly.append(reinterpret_cast<char *>(&insn.k), sizeof(insn.k));
+ assembly.append(reinterpret_cast<char*>(&insn.code), sizeof(insn.code));
+ assembly.append(reinterpret_cast<char*>(&insn.k), sizeof(insn.k));
}
SANDBOX_ASSERT(source == assembly);
}
diff --git a/chromium/sandbox/linux/seccomp-bpf/demo.cc b/chromium/sandbox/linux/seccomp-bpf/demo.cc
index 14180181a6e..d9fd3423eee 100644
--- a/chromium/sandbox/linux/seccomp-bpf/demo.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/demo.cc
@@ -26,12 +26,15 @@
#include <time.h>
#include <unistd.h>
+#include "base/macros.h"
#include "base/posix/eintr_wrapper.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"
using sandbox::ErrorCode;
using sandbox::SandboxBPF;
+using sandbox::SandboxBPFPolicy;
using sandbox::arch_seccomp_data;
#define ERR EPERM
@@ -237,7 +240,17 @@ intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) {
return -ERR;
}
-ErrorCode Evaluator(SandboxBPF* sandbox, int sysno, void *) {
+class DemoPolicy : public SandboxBPFPolicy {
+ public:
+ DemoPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DemoPolicy);
+};
+
+ErrorCode DemoPolicy::EvaluateSyscall(SandboxBPF* sandbox, int sysno) const {
switch (sysno) {
#if defined(__NR_accept)
case __NR_accept: case __NR_accept4:
@@ -420,8 +433,11 @@ int main(int argc, char *argv[]) {
}
SandboxBPF sandbox;
sandbox.set_proc_fd(proc_fd);
- sandbox.SetSandboxPolicyDeprecated(Evaluator, NULL);
- sandbox.StartSandbox();
+ sandbox.SetSandboxPolicy(new DemoPolicy());
+ if (!sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED)) {
+ fprintf(stderr, "StartSandbox() failed");
+ _exit(1);
+ }
// Check that we can create threads
pthread_t thr;
diff --git a/chromium/sandbox/linux/seccomp-bpf/die.cc b/chromium/sandbox/linux/seccomp-bpf/die.cc
index 533e2e9c353..e5bc7c93847 100644
--- a/chromium/sandbox/linux/seccomp-bpf/die.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/die.cc
@@ -22,7 +22,7 @@ void Die::ExitGroup() {
// Especially, since we are dealing with system call filters. Continuing
// execution would be very bad in most cases where ExitGroup() gets called.
// So, we'll try a few other strategies too.
- SandboxSyscall(__NR_exit_group, 1);
+ Syscall::Call(__NR_exit_group, 1);
// We have no idea what our run-time environment looks like. So, signal
// handlers might or might not do the right thing. Try to reset settings
@@ -30,7 +30,7 @@ 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);
+ Syscall::Call(__NR_prctl, PR_SET_DUMPABLE, (void*)0, (void*)0, (void*)0);
if (*(volatile char*)0) {
}
@@ -40,7 +40,7 @@ void Die::ExitGroup() {
// We in fact retry the system call inside of our loop so that it will
// stand out when somebody tries to diagnose the problem by using "strace".
for (;;) {
- SandboxSyscall(__NR_exit_group, 1);
+ Syscall::Call(__NR_exit_group, 1);
}
}
@@ -75,7 +75,7 @@ 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.
ignore_result(
- HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length())));
+ HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length())));
}
}
diff --git a/chromium/sandbox/linux/seccomp-bpf/die.h b/chromium/sandbox/linux/seccomp-bpf/die.h
index 5dcfda0f2b4..3ac31cc6c41 100644
--- a/chromium/sandbox/linux/seccomp-bpf/die.h
+++ b/chromium/sandbox/linux/seccomp-bpf/die.h
@@ -6,6 +6,7 @@
#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
#include "base/basictypes.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -20,7 +21,7 @@ namespace sandbox {
// Adds an informational message to the log file or stderr as appropriate.
#define SANDBOX_INFO(m) sandbox::Die::SandboxInfo(m, __FILE__, __LINE__)
-class Die {
+class SANDBOX_EXPORT Die {
public:
// Terminate the program, even if the current sandbox policy prevents some
// of the more commonly used functions used for exiting.
diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode.cc b/chromium/sandbox/linux/seccomp-bpf/errorcode.cc
index 64848528202..5a45e4c8cf3 100644
--- a/chromium/sandbox/linux/seccomp-bpf/errorcode.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -18,6 +18,11 @@ ErrorCode::ErrorCode(int err) {
error_type_ = ET_SIMPLE;
break;
default:
+ if ((err & ~SECCOMP_RET_DATA) == ERR_TRACE) {
+ err_ = SECCOMP_RET_TRACE + (err & SECCOMP_RET_DATA);
+ error_type_ = ET_SIMPLE;
+ break;
+ }
SANDBOX_DIE("Invalid use of ErrorCode object");
}
}
diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode.h b/chromium/sandbox/linux/seccomp-bpf/errorcode.h
index 625d123513f..2e513818d81 100644
--- a/chromium/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/chromium/sandbox/linux/seccomp-bpf/errorcode.h
@@ -7,6 +7,7 @@
#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -20,7 +21,7 @@ struct arch_seccomp_data;
// All of the commonly used values are stored in the "err_" field. So, code
// that is using the ErrorCode class typically operates on a single 32bit
// field.
-class ErrorCode {
+class SANDBOX_EXPORT ErrorCode {
public:
enum {
// Allow this system call. The value of ERR_ALLOWED is pretty much
@@ -29,6 +30,12 @@ class ErrorCode {
// "errno" (see below) value instead.
ERR_ALLOWED = 0x04000000,
+ // If the progress is being ptraced with PTRACE_O_TRACESECCOMP, then the
+ // tracer will be notified of a PTRACE_EVENT_SECCOMP and allowed to change
+ // or skip the system call. The lower 16 bits of err will be available to
+ // the tracer via PTRACE_GETEVENTMSG.
+ ERR_TRACE = 0x08000000,
+
// Deny the system call with a particular "errno" value.
// N.B.: It is also possible to return "0" here. That would normally
// indicate success, but it won't actually run the system call.
diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
index ef04a5f61f8..f3b77483355 100644
--- a/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -24,6 +24,17 @@ SANDBOX_TEST(ErrorCode, ErrnoConstructor) {
SandboxBPF sandbox;
ErrorCode e3 = sandbox.Trap(NULL, NULL);
SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP);
+
+ uint16_t data = 0xdead;
+ ErrorCode e4(ErrorCode::ERR_TRACE + data);
+ SANDBOX_ASSERT(e4.err() == SECCOMP_RET_TRACE + data);
+}
+
+SANDBOX_DEATH_TEST(ErrorCode,
+ InvalidSeccompRetTrace,
+ DEATH_MESSAGE("Invalid use of ErrorCode object")) {
+ // Should die if the trace data does not fit in 16 bits.
+ ErrorCode e(ErrorCode::ERR_TRACE + (1 << 16));
}
SANDBOX_TEST(ErrorCode, Trap) {
diff --git a/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h b/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h
index 0de0259da39..270e11c1f8f 100644
--- a/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h
+++ b/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h
@@ -16,6 +16,14 @@
#include <asm/unistd.h>
#include <linux/filter.h>
+#include <sys/cdefs.h>
+// Old Bionic versions do not have sys/user.h. The if can be removed once we no
+// longer need to support these old Bionic versions.
+// All x86_64 builds use a new enough bionic to have sys/user.h.
+#if !defined(__BIONIC__) || defined(__x86_64__)
+#include <sys/user.h>
+#endif
+
// For audit.h
#ifndef EM_ARM
#define EM_ARM 40
@@ -124,6 +132,44 @@
#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
8*(nr) + 0)
+
+#if defined(__BIONIC__)
+// Old Bionic versions don't have sys/user.h, so we just define regs_struct
+// directly. This can be removed once we no longer need to support these old
+// Bionic versions.
+struct regs_struct {
+ long int ebx;
+ long int ecx;
+ long int edx;
+ long int esi;
+ long int edi;
+ long int ebp;
+ long int eax;
+ long int xds;
+ long int xes;
+ long int xfs;
+ long int xgs;
+ long int orig_eax;
+ long int eip;
+ long int xcs;
+ long int eflags;
+ long int esp;
+ long int xss;
+};
+#else
+typedef user_regs_struct regs_struct;
+#endif
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).eax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_eax
+#define SECCOMP_PT_IP(_regs) (_regs).eip
+#define SECCOMP_PT_PARM1(_regs) (_regs).ebx
+#define SECCOMP_PT_PARM2(_regs) (_regs).ecx
+#define SECCOMP_PT_PARM3(_regs) (_regs).edx
+#define SECCOMP_PT_PARM4(_regs) (_regs).esi
+#define SECCOMP_PT_PARM5(_regs) (_regs).edi
+#define SECCOMP_PT_PARM6(_regs) (_regs).ebp
+
#elif defined(__x86_64__)
#define MIN_SYSCALL 0u
#define MAX_PUBLIC_SYSCALL 1024u
@@ -151,6 +197,17 @@
#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
8*(nr) + 0)
+typedef user_regs_struct regs_struct;
+#define SECCOMP_PT_RESULT(_regs) (_regs).rax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_rax
+#define SECCOMP_PT_IP(_regs) (_regs).rip
+#define SECCOMP_PT_PARM1(_regs) (_regs).rdi
+#define SECCOMP_PT_PARM2(_regs) (_regs).rsi
+#define SECCOMP_PT_PARM3(_regs) (_regs).rdx
+#define SECCOMP_PT_PARM4(_regs) (_regs).r10
+#define SECCOMP_PT_PARM5(_regs) (_regs).r8
+#define SECCOMP_PT_PARM6(_regs) (_regs).r9
+
#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
// and a "ghost syscall private to the kernel", cmpxchg,
@@ -189,6 +246,46 @@
#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
8*(nr) + 0)
+#if defined(__BIONIC__)
+// Old Bionic versions don't have sys/user.h, so we just define regs_struct
+// directly. This can be removed once we no longer need to support these old
+// Bionic versions.
+struct regs_struct {
+ unsigned long uregs[18];
+};
+#else
+typedef user_regs regs_struct;
+#endif
+
+#define REG_cpsr uregs[16]
+#define REG_pc uregs[15]
+#define REG_lr uregs[14]
+#define REG_sp uregs[13]
+#define REG_ip uregs[12]
+#define REG_fp uregs[11]
+#define REG_r10 uregs[10]
+#define REG_r9 uregs[9]
+#define REG_r8 uregs[8]
+#define REG_r7 uregs[7]
+#define REG_r6 uregs[6]
+#define REG_r5 uregs[5]
+#define REG_r4 uregs[4]
+#define REG_r3 uregs[3]
+#define REG_r2 uregs[2]
+#define REG_r1 uregs[1]
+#define REG_r0 uregs[0]
+#define REG_ORIG_r0 uregs[17]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_r0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_r7
+#define SECCOMP_PT_IP(_regs) (_regs).REG_pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_r0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_r1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_r2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_r3
+#define SECCOMP_PT_PARM5(_regs) (_regs).REG_r4
+#define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5
+
#else
#error Unsupported target platform
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 6b2327e5452..6ecbca99a5f 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -22,6 +22,7 @@
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "sandbox/linux/seccomp-bpf/codegen.h"
@@ -56,20 +57,26 @@ void WriteFailedStderrSetupMessage(int out_fd) {
// 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(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);
+class ProbePolicy : public SandboxBPFPolicy {
+ public:
+ ProbePolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysnum) const OVERRIDE {
+ 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);
+ }
}
-}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProbePolicy);
+};
void ProbeProcess(void) {
if (syscall(__NR_getpid) < 0 && errno == EPERM) {
@@ -77,12 +84,17 @@ void ProbeProcess(void) {
}
}
-ErrorCode AllowAllEvaluator(SandboxBPF*, int sysnum, void*) {
- if (!SandboxBPF::IsValidSyscallNumber(sysnum)) {
- return ErrorCode(ENOSYS);
+class AllowAllPolicy : public SandboxBPFPolicy {
+ public:
+ AllowAllPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysnum) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysnum));
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
- return ErrorCode(ErrorCode::ERR_ALLOWED);
-}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
+};
void TryVsyscallProcess(void) {
time_t current_time;
@@ -187,13 +199,21 @@ class RedirectToUserSpacePolicyWrapper : public SandboxBPFPolicy {
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 ReturnErrnoViaTrap(sandbox_compiler, err.err() & SECCOMP_RET_DATA);
}
return err;
}
+ virtual ErrorCode InvalidSyscall(
+ SandboxBPF* sandbox_compiler) const OVERRIDE {
+ return ReturnErrnoViaTrap(sandbox_compiler, ENOSYS);
+ }
+
private:
+ ErrorCode ReturnErrnoViaTrap(SandboxBPF* sandbox_compiler, int err) const {
+ return sandbox_compiler->Trap(ReturnErrno, reinterpret_cast<void*>(err));
+ }
+
const SandboxBPFPolicy* wrapped_policy_;
DISALLOW_COPY_AND_ASSIGN(RedirectToUserSpacePolicyWrapper);
};
@@ -202,25 +222,6 @@ intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
SANDBOX_DIE(static_cast<char*>(aux));
}
-// 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_);
- }
-
- private:
- SandboxBPF::EvaluateSyscall syscall_evaluator_;
- void* aux_;
- DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy);
-};
-
} // namespace
SandboxBPF::SandboxBPF()
@@ -251,8 +252,7 @@ bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
}
bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(),
- EvaluateSyscall syscall_evaluator,
- void* aux) {
+ scoped_ptr<SandboxBPFPolicy> policy) {
// 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;
@@ -322,8 +322,10 @@ bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(),
#endif
}
- SetSandboxPolicyDeprecated(syscall_evaluator, aux);
- StartSandbox();
+ SetSandboxPolicy(policy.release());
+ if (!StartSandbox(PROCESS_SINGLE_THREADED)) {
+ SANDBOX_DIE(NULL);
+ }
// Run our code in the sandbox.
code_in_sandbox();
@@ -369,8 +371,11 @@ bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(),
}
bool SandboxBPF::KernelSupportSeccompBPF() {
- return RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) &&
- RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0);
+ return RunFunctionInPolicy(ProbeProcess,
+ scoped_ptr<SandboxBPFPolicy>(new ProbePolicy())) &&
+ RunFunctionInPolicy(
+ TryVsyscallProcess,
+ scoped_ptr<SandboxBPFPolicy>(new AllowAllPolicy()));
}
SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) {
@@ -430,15 +435,20 @@ SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) {
void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; }
-void SandboxBPF::StartSandbox() {
+bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) {
+ CHECK(thread_state == PROCESS_SINGLE_THREADED ||
+ thread_state == PROCESS_MULTI_THREADED);
+
if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
SANDBOX_DIE(
"Trying to start sandbox, even though it is known to be "
"unavailable");
+ return false;
} else if (sandbox_has_started_ || !conds_) {
SANDBOX_DIE(
"Cannot repeatedly start sandbox. Create a separate Sandbox "
"object instead.");
+ return false;
}
if (proc_fd_ < 0) {
proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY);
@@ -447,8 +457,10 @@ void SandboxBPF::StartSandbox() {
// For now, continue in degraded mode, if we can't access /proc.
// In the future, we might want to tighten this requirement.
}
- if (!IsSingleThreaded(proc_fd_)) {
+
+ if (thread_state == PROCESS_SINGLE_THREADED && !IsSingleThreaded(proc_fd_)) {
SANDBOX_DIE("Cannot start sandbox, if process is already multi-threaded");
+ return false;
}
// We no longer need access to any files in /proc. We want to do this
@@ -457,38 +469,27 @@ void SandboxBPF::StartSandbox() {
if (proc_fd_ >= 0) {
if (IGNORE_EINTR(close(proc_fd_))) {
SANDBOX_DIE("Failed to close file descriptor for /proc");
+ return false;
}
proc_fd_ = -1;
}
// Install the filters.
- InstallFilter();
+ InstallFilter(thread_state);
// We are now inside the sandbox.
status_ = STATUS_ENABLED;
+
+ return true;
}
void SandboxBPF::PolicySanityChecks(SandboxBPFPolicy* policy) {
- for (SyscallIterator iter(true); !iter.Done();) {
- uint32_t sysnum = iter.Next();
- if (!IsDenied(policy->EvaluateSyscall(this, sysnum))) {
- SANDBOX_DIE(
- "Policies should deny system calls that are outside the "
- "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
- }
+ if (!IsDenied(policy->InvalidSyscall(this))) {
+ SANDBOX_DIE("Policies should deny invalid system calls.");
}
return;
}
-// 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");
- }
- SetSandboxPolicy(new CompatibilityPolicy(syscall_evaluator, aux));
-}
-
// Don't take a scoped_ptr here, polymorphism make their use awkward.
void SandboxBPF::SetSandboxPolicy(SandboxBPFPolicy* policy) {
DCHECK(!policy_);
@@ -499,7 +500,7 @@ void SandboxBPF::SetSandboxPolicy(SandboxBPFPolicy* policy) {
policy_.reset(policy);
}
-void SandboxBPF::InstallFilter() {
+void SandboxBPF::InstallFilter(SandboxThreadState thread_state) {
// 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
@@ -535,9 +536,23 @@ void SandboxBPF::InstallFilter() {
}
}
- sandbox_has_started_ = true;
+ // TODO(rsesek): Always try to engage the sandbox with the
+ // PROCESS_MULTI_THREADED path first, and if that fails, assert that the
+ // process IsSingleThreaded() or SANDBOX_DIE.
+
+ if (thread_state == PROCESS_MULTI_THREADED) {
+ // TODO(rsesek): Move these to a more reasonable place once the kernel
+ // patch has landed upstream and these values are formalized.
+ #define PR_SECCOMP_EXT 41
+ #define SECCOMP_EXT_ACT 1
+ #define SECCOMP_EXT_ACT_TSYNC 1
+ if (prctl(PR_SECCOMP_EXT, SECCOMP_EXT_ACT, SECCOMP_EXT_ACT_TSYNC, 0, 0)) {
+ SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to synchronize threadgroup "
+ "BPF filters.");
+ }
+ }
- return;
+ sandbox_has_started_ = true;
}
SandboxBPF::Program* SandboxBPF::AssembleFilter(bool force_verification) {
@@ -598,7 +613,7 @@ SandboxBPF::Program* SandboxBPF::AssembleFilter(bool force_verification) {
// and of course, we make sure to only ever enable this feature if it
// is actually requested by the sandbox policy.
if (has_unsafe_traps) {
- if (SandboxSyscall(-1) == -1 && errno == ENOSYS) {
+ if (Syscall::Call(-1) == -1 && errno == ENOSYS) {
SANDBOX_DIE(
"Support for UnsafeTrap() has not yet been ported to this "
"architecture");
@@ -635,9 +650,8 @@ SandboxBPF::Program* SandboxBPF::AssembleFilter(bool force_verification) {
gen->Traverse(jumptable, RedirectToUserspace, this);
// 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));
+ // (which we can query by calling Syscall::Call(-1)).
+ uintptr_t syscall_entry_point = static_cast<uintptr_t>(Syscall::Call(-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);
@@ -737,20 +751,18 @@ void SandboxBPF::FindRanges(Ranges* ranges) {
// 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.
+ const ErrorCode invalid_err = policy_->InvalidSyscall(this);
uint32_t old_sysnum = 0;
- ErrorCode old_err = policy_->EvaluateSyscall(this, old_sysnum);
- ErrorCode invalid_err = policy_->EvaluateSyscall(this, MIN_SYSCALL - 1);
+ ErrorCode old_err = IsValidSyscallNumber(old_sysnum)
+ ? policy_->EvaluateSyscall(this, old_sysnum)
+ : invalid_err;
for (SyscallIterator iter(false); !iter.Done();) {
uint32_t sysnum = iter.Next();
- 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
- // "false" for SyscallIterator::IsValid()) identically. Typically, all
- // of these system calls would be denied with the same ErrorCode.
- SANDBOX_DIE("Invalid seccomp policy");
- }
+ ErrorCode err =
+ IsValidSyscallNumber(sysnum)
+ ? policy_->EvaluateSyscall(this, static_cast<int>(sysnum))
+ : invalid_err;
if (!err.Equals(old_err) || iter.Done()) {
ranges->push_back(Range(old_sysnum, sysnum - 1, old_err));
old_sysnum = sysnum;
@@ -990,13 +1002,13 @@ ErrorCode SandboxBPF::UnsafeTrap(Trap::TrapFnc fnc, const void* aux) {
}
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]),
- static_cast<intptr_t>(args.args[2]),
- static_cast<intptr_t>(args.args[3]),
- static_cast<intptr_t>(args.args[4]),
- static_cast<intptr_t>(args.args[5]));
+ return Syscall::Call(args.nr,
+ static_cast<intptr_t>(args.args[0]),
+ static_cast<intptr_t>(args.args[1]),
+ static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]),
+ static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
}
ErrorCode SandboxBPF::Cond(int argno,
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index d626e4c74ca..32fe2a7d748 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -16,13 +16,16 @@
#include <utility>
#include <vector>
+#include "base/compiler_specific.h"
#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/sandbox_export.h"
namespace sandbox {
+// This must match the kernel's seccomp_data structure.
struct arch_seccomp_data {
int nr;
uint32_t arch;
@@ -41,7 +44,7 @@ class SandboxBPFPolicy;
class SandboxUnittestHelper;
struct Instruction;
-class SandboxBPF {
+class SANDBOX_EXPORT SandboxBPF {
public:
enum SandboxStatus {
STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox()
@@ -51,15 +54,18 @@ class SandboxBPF {
STATUS_ENABLED // The sandbox is now active
};
- // 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 ErrorCode (*EvaluateSyscall)(SandboxBPF* sandbox_compiler,
- int system_call_number,
- void* aux);
- typedef std::vector<std::pair<EvaluateSyscall, void*> > Evaluators;
+ // Depending on the level of kernel support, seccomp-bpf may require the
+ // process to be single-threaded in order to enable it. When calling
+ // StartSandbox(), the program should indicate whether or not the sandbox
+ // should try and engage with multi-thread support.
+ enum SandboxThreadState {
+ PROCESS_INVALID,
+ PROCESS_SINGLE_THREADED, // The program is currently single-threaded.
+ // Note: PROCESS_MULTI_THREADED requires experimental kernel support that
+ // has not been contributed to upstream Linux.
+ PROCESS_MULTI_THREADED, // The program may be multi-threaded.
+ };
+
// A vector of BPF instructions that need to be installed as a filter
// program in the kernel.
typedef std::vector<struct sock_filter> Program;
@@ -96,20 +102,6 @@ class SandboxBPF {
// eventually close it when "StartSandbox()" executes.
void set_proc_fd(int proc_fd);
- // The system call evaluator function 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.
- // The "aux" parameter can be used to pass optional data to the system call
- // evaluator. There are different possible uses for this data, but one of the
- // use cases would be for the policy to then forward this pointer to a Trap()
- // 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.
- // 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);
@@ -167,6 +159,8 @@ class SandboxBPF {
// This is the main public entry point. It finds all system calls that
// need rewriting, sets up the resources needed by the sandbox, and
// enters Seccomp mode.
+ // The calling process must specify its current SandboxThreadState, as a way
+ // to tell the sandbox which type of kernel support it should engage.
// It is possible to stack multiple sandboxes by creating separate "Sandbox"
// objects and calling "StartSandbox()" on each of them. Please note, that
// this requires special care, though, as newly stacked sandboxes can never
@@ -175,7 +169,7 @@ class SandboxBPF {
// disallowed.
// Finally, stacking does add more kernel overhead than having a single
// combined policy. So, it should only be used if there are no alternatives.
- void StartSandbox();
+ bool StartSandbox(SandboxThreadState thread_state) WARN_UNUSED_RESULT;
// Assembles a BPF filter program from the current policy. After calling this
// function, you must not call any other sandboxing function.
@@ -214,8 +208,7 @@ class SandboxBPF {
// 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);
+ scoped_ptr<SandboxBPFPolicy> policy);
// Performs a couple of sanity checks to verify that the kernel supports the
// features that we need for successful sandboxing.
@@ -228,7 +221,7 @@ class SandboxBPF {
// Assembles and installs a filter based on the policy that has previously
// been configured with SetSandboxPolicy().
- void InstallFilter();
+ void InstallFilter(SandboxThreadState thread_state);
// Verify the correctness of a compiled program by comparing it against the
// current policy. This function should only ever be called by unit tests and
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h
new file mode 100644
index 00000000000..d4b8ab8b15a
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h
@@ -0,0 +1,43 @@
+// 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_SECCOMP_BPF_SANDBOX_BPF_COMPATIBILITY_POLICY_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_COMPATIBILITY_POLICY_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
+
+namespace sandbox {
+
+// This class allows compatibility with the old, deprecated
+// policies that were designed for SetSandboxPolicyDeprecated().
+template <class AuxType>
+class CompatibilityPolicy : public SandboxBPFPolicy {
+ public:
+ typedef ErrorCode (*SyscallEvaluator)(SandboxBPF* sandbox_compiler,
+ int system_call_number,
+ AuxType* aux);
+ CompatibilityPolicy(SyscallEvaluator syscall_evaluator, AuxType* aux)
+ : syscall_evaluator_(syscall_evaluator), aux_(aux) {}
+
+ virtual ~CompatibilityPolicy() {}
+
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
+ int system_call_number) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
+ return syscall_evaluator_(sandbox_compiler, system_call_number, aux_);
+ }
+
+ private:
+ SyscallEvaluator syscall_evaluator_;
+ AuxType* aux_;
+ DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_COMPATIBILITY_POLICY_H_
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.cc
new file mode 100644
index 00000000000..962c8036f8a
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.cc
@@ -0,0 +1,17 @@
+// 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/seccomp-bpf/sandbox_bpf_policy.h"
+
+#include <errno.h>
+
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+namespace sandbox {
+
+ErrorCode SandboxBPFPolicy::InvalidSyscall(SandboxBPF* sandbox_compiler) const {
+ return ErrorCode(ENOSYS);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h
index 1ac5daba5d9..fc6fdf6fe4f 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h
@@ -6,6 +6,7 @@
#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_
#include "base/basictypes.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -13,7 +14,7 @@ class ErrorCode;
class SandboxBPF;
// This is the interface to implement to define a BPF sandbox policy.
-class SandboxBPFPolicy {
+class SANDBOX_EXPORT SandboxBPFPolicy {
public:
SandboxBPFPolicy() {}
virtual ~SandboxBPFPolicy() {}
@@ -23,9 +24,14 @@ class SandboxBPFPolicy {
// 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.
+ // Will only be called for valid system call numbers.
virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
int system_call_number) const = 0;
+ // The InvalidSyscall method specifies the behavior used for invalid
+ // system calls. The default implementation is to return ENOSYS.
+ virtual ErrorCode InvalidSyscall(SandboxBPF* sandbox_compiler) const;
+
private:
DISALLOW_COPY_AND_ASSIGN(SandboxBPFPolicy);
};
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
new file mode 100644
index 00000000000..ff659ab7f80
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -0,0 +1,76 @@
+// 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/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+SandboxBPFTestRunner::SandboxBPFTestRunner(
+ BPFTesterDelegate* bpf_tester_delegate)
+ : bpf_tester_delegate_(bpf_tester_delegate) {
+}
+
+SandboxBPFTestRunner::~SandboxBPFTestRunner() {
+}
+
+void SandboxBPFTestRunner::Run() {
+ DCHECK(bpf_tester_delegate_);
+ sandbox::Die::EnableSimpleExit();
+
+ scoped_ptr<SandboxBPFPolicy> policy =
+ bpf_tester_delegate_->GetSandboxBPFPolicy();
+
+ if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) ==
+ sandbox::SandboxBPF::STATUS_AVAILABLE) {
+ // Ensure the the sandbox is actually available at this time
+ int proc_fd;
+ SANDBOX_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0);
+ SANDBOX_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) ==
+ sandbox::SandboxBPF::STATUS_AVAILABLE);
+
+ // Initialize and then start the sandbox with our custom policy
+ sandbox::SandboxBPF sandbox;
+ sandbox.set_proc_fd(proc_fd);
+ sandbox.SetSandboxPolicy(policy.release());
+ SANDBOX_ASSERT(
+ sandbox.StartSandbox(sandbox::SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ // Run the actual test.
+ bpf_tester_delegate_->RunTestFunction();
+ } else {
+ printf("This BPF test is not fully running in this configuration!\n");
+ // Android and Valgrind are the only configurations where we accept not
+ // having kernel BPF support.
+ if (!IsAndroid() && !IsRunningOnValgrind()) {
+ const bool seccomp_bpf_is_supported = false;
+ SANDBOX_ASSERT(seccomp_bpf_is_supported);
+ }
+ // Call the compiler and verify the policy. That's the least we can do,
+ // if we don't have kernel support.
+ sandbox::SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(policy.release());
+ sandbox::SandboxBPF::Program* program =
+ sandbox.AssembleFilter(true /* force_verification */);
+ delete program;
+ sandbox::UnitTests::IgnoreThisTest();
+ }
+}
+
+bool SandboxBPFTestRunner::ShouldCheckForLeaks() const {
+ // LSAN requires being able to use ptrace() and other system calls that could
+ // be denied.
+ return false;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
new file mode 100644
index 00000000000..77210330414
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
@@ -0,0 +1,59 @@
+// 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_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+// To create a SandboxBPFTestRunner object, one needs to implement this
+// interface and pass an instance to the SandboxBPFTestRunner constructor.
+// In the child process running the test, the BPFTesterDelegate object is
+// guaranteed to not be destroyed until the child process terminates.
+class BPFTesterDelegate {
+ public:
+ BPFTesterDelegate() {}
+ virtual ~BPFTesterDelegate() {}
+
+ // This will instanciate a policy suitable for the test we want to run. It is
+ // guaranteed to only be called from the child process that will run the
+ // test.
+ virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() = 0;
+ // This will be called from a child process with the BPF sandbox turned on.
+ virtual void RunTestFunction() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterDelegate);
+};
+
+// This class implements the SandboxTestRunner interface and Run() will
+// initialize a seccomp-bpf sandbox (specified by |bpf_tester_delegate|) and
+// run a test function (via |bpf_tester_delegate|) if the current kernel
+// configuration allows it. If it can not run the test under seccomp-bpf,
+// Run() will still compile the policy which should allow to get some coverage
+// under tools such as Valgrind.
+class SandboxBPFTestRunner : public SandboxTestRunner {
+ public:
+ // This constructor takes ownership of the |bpf_tester_delegate| object.
+ // (It doesn't take a scoped_ptr since they make polymorphism verbose).
+ explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate);
+ virtual ~SandboxBPFTestRunner();
+
+ virtual void Run() OVERRIDE;
+
+ virtual bool ShouldCheckForLeaks() const OVERRIDE;
+
+ private:
+ scoped_ptr<BPFTesterDelegate> bpf_tester_delegate_;
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPFTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 988e29544b0..06ba2090c91 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -5,7 +5,9 @@
#include <errno.h>
#include <pthread.h>
#include <sched.h>
+#include <signal.h>
#include <sys/prctl.h>
+#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -20,7 +22,11 @@
#include <ostream>
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
@@ -49,7 +55,7 @@ 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, DISABLE_ON_TSAN(CallSupports)) {
// We check that we don't crash, but it's ok if the kernel doesn't
// support it.
bool seccomp_bpf_supported =
@@ -64,7 +70,7 @@ TEST(SandboxBPF, CallSupports) {
std::cout << "Pointer size: " << sizeof(void*) << "\n";
}
-SANDBOX_TEST(SandboxBPF, CallSupportsTwice) {
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupportsTwice)) {
SandboxBPF::SupportsSeccompSandbox(-1);
SandboxBPF::SupportsSeccompSandbox(-1);
}
@@ -76,59 +82,67 @@ 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 IncreaseCounter(const struct arch_seccomp_data& args, void* aux) {
BPF_ASSERT(aux);
- pid_t* pid_ptr = static_cast<pid_t*>(aux);
- return (*pid_ptr)++;
+ int* counter = static_cast<int*>(aux);
+ return (*counter)++;
}
-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);
- } else {
+class VerboseAPITestingPolicy : public SandboxBPFPolicy {
+ public:
+ VerboseAPITestingPolicy(int* counter_ptr) : counter_ptr_(counter_ptr) {}
+
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return sandbox->Trap(IncreaseCounter, counter_ptr_);
+ }
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-}
+
+ private:
+ int* counter_ptr_;
+ DISALLOW_COPY_AND_ASSIGN(VerboseAPITestingPolicy);
+};
SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) {
if (SandboxBPF::SupportsSeccompSandbox(-1) ==
sandbox::SandboxBPF::STATUS_AVAILABLE) {
- pid_t test_var = 0;
+ static int counter = 0;
+
SandboxBPF sandbox;
- sandbox.SetSandboxPolicyDeprecated(VerboseAPITestingPolicy, &test_var);
- sandbox.StartSandbox();
-
- BPF_ASSERT(test_var == 0);
- BPF_ASSERT(syscall(__NR_getpid) == 0);
- BPF_ASSERT(test_var == 1);
- BPF_ASSERT(syscall(__NR_getpid) == 1);
- BPF_ASSERT(test_var == 2);
-
- // N.B.: Any future call to getpid() would corrupt the stack.
- // This is OK. The SANDBOX_TEST() macro is guaranteed to
- // only ever call _exit() after the test completes.
+ sandbox.SetSandboxPolicy(new VerboseAPITestingPolicy(&counter));
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ BPF_ASSERT_EQ(0, counter);
+ BPF_ASSERT_EQ(0, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(1, counter);
+ BPF_ASSERT_EQ(1, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(2, counter);
}
}
// A simple blacklist test
-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);
+class BlacklistNanosleepPolicy : public SandboxBPFPolicy {
+ public:
+ BlacklistNanosleepPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_nanosleep:
+ return ErrorCode(EACCES);
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
}
- switch (sysno) {
- case __NR_nanosleep:
- return ErrorCode(EACCES);
- default:
- return ErrorCode(ErrorCode::ERR_ALLOWED);
- }
-}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BlacklistNanosleepPolicy);
+};
-BPF_TEST(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
+BPF_TEST_C(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
// nanosleep() should be denied
const struct timespec ts = {0, 0};
errno = 0;
@@ -138,17 +152,25 @@ BPF_TEST(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
// Now do a simple whitelist test
-ErrorCode WhitelistGetpidPolicy(SandboxBPF*, int sysno, void*) {
- switch (sysno) {
- case __NR_getpid:
- case __NR_exit_group:
- return ErrorCode(ErrorCode::ERR_ALLOWED);
- default:
- return ErrorCode(ENOMEM);
+class WhitelistGetpidPolicy : public SandboxBPFPolicy {
+ public:
+ WhitelistGetpidPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getpid:
+ case __NR_exit_group:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ default:
+ return ErrorCode(ENOMEM);
+ }
}
-}
-BPF_TEST(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WhitelistGetpidPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
// getpid() should be allowed
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
@@ -160,7 +182,6 @@ BPF_TEST(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
}
// A simple blacklist policy, with a SIGSYS handler
-
intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) {
// We also check that the auxiliary data is correct
SANDBOX_ASSERT(aux);
@@ -170,12 +191,8 @@ intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) {
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);
- }
-
+ int* aux) {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
switch (sysno) {
case __NR_nanosleep:
return sandbox->Trap(EnomemHandler, aux);
@@ -187,32 +204,41 @@ ErrorCode BlacklistNanosleepPolicySigsys(SandboxBPF* sandbox,
BPF_TEST(SandboxBPF,
BasicBlacklistWithSigsys,
BlacklistNanosleepPolicySigsys,
- int /* BPF_AUX */) {
+ int /* (*BPF_AUX) */) {
// getpid() should work properly
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
BPF_ASSERT(errno == 0);
// Our Auxiliary Data, should be reset by the signal handler
- BPF_AUX = -1;
+ *BPF_AUX = -1;
const struct timespec ts = {0, 0};
BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
BPF_ASSERT(errno == ENOMEM);
// We expect the signal handler to modify AuxData
- BPF_ASSERT(BPF_AUX == kExpectedReturnValue);
+ BPF_ASSERT(*BPF_AUX == kExpectedReturnValue);
}
// A simple test that verifies we can return arbitrary errno values.
-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);
- }
+class ErrnoTestPolicy : public SandboxBPFPolicy {
+ public:
+ ErrnoTestPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysno) const OVERRIDE;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ErrnoTestPolicy);
+};
+
+ErrorCode ErrnoTestPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
switch (sysno) {
+#if defined(ANDROID)
+ case __NR_dup3: // dup2 is a wrapper of dup3 in android
+#else
case __NR_dup2:
+#endif
// Pretend that dup2() worked, but don't actually do anything.
return ErrorCode(0);
case __NR_setuid:
@@ -235,7 +261,7 @@ ErrorCode ErrnoTestPolicy(SandboxBPF*, int sysno, void*) {
}
}
-BPF_TEST(SandboxBPF, ErrnoTest, ErrnoTestPolicy) {
+BPF_TEST_C(SandboxBPF, ErrnoTest, ErrnoTestPolicy) {
// Verify that dup2() returns success, but doesn't actually run.
int fds[4];
BPF_ASSERT(pipe(fds) == 0);
@@ -277,43 +303,53 @@ BPF_TEST(SandboxBPF, ErrnoTest, ErrnoTestPolicy) {
// Testing the stacking of two sandboxes
-ErrorCode StackingPolicyPartOne(SandboxBPF* sandbox, int sysno, void*) {
- if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
- return ErrorCode(ENOSYS);
+class StackingPolicyPartOne : public SandboxBPFPolicy {
+ public:
+ StackingPolicyPartOne() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getppid:
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ ErrorCode(EPERM));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
}
- switch (sysno) {
- case __NR_getppid:
- return sandbox->Cond(0,
- ErrorCode::TP_32BIT,
- ErrorCode::OP_EQUAL,
- 0,
- ErrorCode(ErrorCode::ERR_ALLOWED),
- ErrorCode(EPERM));
- default:
- return ErrorCode(ErrorCode::ERR_ALLOWED);
- }
-}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackingPolicyPartOne);
+};
-ErrorCode StackingPolicyPartTwo(SandboxBPF* sandbox, int sysno, void*) {
- if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
- return ErrorCode(ENOSYS);
+class StackingPolicyPartTwo : public SandboxBPFPolicy {
+ public:
+ StackingPolicyPartTwo() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getppid:
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0,
+ ErrorCode(EINVAL),
+ ErrorCode(ErrorCode::ERR_ALLOWED));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
}
- switch (sysno) {
- case __NR_getppid:
- return sandbox->Cond(0,
- ErrorCode::TP_32BIT,
- ErrorCode::OP_EQUAL,
- 0,
- ErrorCode(EINVAL),
- ErrorCode(ErrorCode::ERR_ALLOWED));
- default:
- return ErrorCode(ErrorCode::ERR_ALLOWED);
- }
-}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackingPolicyPartTwo);
+};
-BPF_TEST(SandboxBPF, StackingPolicy, StackingPolicyPartOne) {
+BPF_TEST_C(SandboxBPF, StackingPolicy, StackingPolicyPartOne) {
errno = 0;
BPF_ASSERT(syscall(__NR_getppid, 0) > 0);
BPF_ASSERT(errno == 0);
@@ -324,8 +360,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.
SandboxBPF sandbox;
- sandbox.SetSandboxPolicyDeprecated(StackingPolicyPartTwo, NULL);
- sandbox.StartSandbox();
+ sandbox.SetSandboxPolicy(new StackingPolicyPartTwo());
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
errno = 0;
BPF_ASSERT(syscall(__NR_getppid, 0) == -1);
@@ -350,29 +386,24 @@ int SysnoToRandomErrno(int sysno) {
return ((sysno & ~3) >> 2) % 29 + 1;
}
-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);
- }
-
-// TODO(jorgelo): remove this once the new code generator lands.
-#if defined(__arm__)
- if (sysno > static_cast<int>(MAX_PUBLIC_SYSCALL)) {
- return ErrorCode(ENOSYS);
- }
-#endif
-
- if (sysno == __NR_exit_group || sysno == __NR_write) {
- // exit_group() is special, we really need it to work.
- // write() is needed for BPF_ASSERT() to report a useful error message.
- return ErrorCode(ErrorCode::ERR_ALLOWED);
- } else {
+class SyntheticPolicy : public SandboxBPFPolicy {
+ public:
+ SyntheticPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_exit_group || sysno == __NR_write) {
+ // exit_group() is special, we really need it to work.
+ // write() is needed for BPF_ASSERT() to report a useful error message.
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
return ErrorCode(SysnoToRandomErrno(sysno));
}
-}
-BPF_TEST(SandboxBPF, SyntheticPolicy, SyntheticPolicy) {
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyntheticPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, SyntheticPolicy, SyntheticPolicy) {
// Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
// overflow.
BPF_ASSERT(std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
@@ -406,23 +437,25 @@ int ArmPrivateSysnoToErrno(int 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);
- }
-
- // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
- // ARM private system calls.
- if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
- sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
- return ErrorCode(ArmPrivateSysnoToErrno(sysno));
- } else {
+class ArmPrivatePolicy : public SandboxBPFPolicy {
+ public:
+ ArmPrivatePolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF*, int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
+ // ARM private system calls.
+ if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return ErrorCode(ArmPrivateSysnoToErrno(sysno));
+ }
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-}
-BPF_TEST(SandboxBPF, ArmPrivatePolicy, ArmPrivatePolicy) {
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArmPrivatePolicy);
+};
+
+BPF_TEST_C(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) {
@@ -446,7 +479,7 @@ intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) {
return SandboxBPF::ForwardSyscall(args);
}
-ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
+ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, int* 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
@@ -479,12 +512,12 @@ ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
}
}
-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(*BPF_AUX == 0);
BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid));
- BPF_ASSERT(BPF_AUX == 2);
+ BPF_ASSERT(*BPF_AUX == 2);
char name[17] = {};
BPF_ASSERT(!syscall(__NR_prctl,
PR_GET_NAME,
@@ -492,7 +525,7 @@ BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* BPF_AUX */) {
(void*)NULL,
(void*)NULL,
(void*)NULL));
- BPF_ASSERT(BPF_AUX == 3);
+ BPF_ASSERT(*BPF_AUX == 3);
BPF_ASSERT(*name);
}
@@ -519,22 +552,29 @@ intptr_t PrctlHandler(const struct arch_seccomp_data& args, void*) {
}
}
-ErrorCode PrctlPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
- setenv(kSandboxDebuggingEnv, "t", 0);
- Die::SuppressInfoMessages(true);
+class PrctlPolicy : public SandboxBPFPolicy {
+ public:
+ PrctlPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ if (sysno == __NR_prctl) {
+ // Handle prctl() inside an UnsafeTrap()
+ return sandbox->UnsafeTrap(PrctlHandler, NULL);
+ }
- if (sysno == __NR_prctl) {
- // Handle prctl() inside an UnsafeTrap()
- return sandbox->UnsafeTrap(PrctlHandler, NULL);
- } else if (SandboxBPF::IsValidSyscallNumber(sysno)) {
// Allow all other system calls.
return ErrorCode(ErrorCode::ERR_ALLOWED);
- } else {
- return ErrorCode(ENOSYS);
}
-}
-BPF_TEST(SandboxBPF, ForwardSyscall, PrctlPolicy) {
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrctlPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ForwardSyscall, PrctlPolicy) {
// This call should never be allowed. But our policy will intercept it and
// let it pass successfully.
BPF_ASSERT(
@@ -565,7 +605,19 @@ intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void*) {
return SandboxBPF::ForwardSyscall(args);
}
-ErrorCode RedirectAllSyscallsPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
+class RedirectAllSyscallsPolicy : public SandboxBPFPolicy {
+ public:
+ RedirectAllSyscallsPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RedirectAllSyscallsPolicy);
+};
+
+ErrorCode RedirectAllSyscallsPolicy::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
setenv(kSandboxDebuggingEnv, "t", 0);
Die::SuppressInfoMessages(true);
@@ -582,11 +634,8 @@ ErrorCode RedirectAllSyscallsPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
#endif
) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
- } else if (SandboxBPF::IsValidSyscallNumber(sysno)) {
- return sandbox->UnsafeTrap(AllowRedirectedSyscall, aux);
- } else {
- return ErrorCode(ENOSYS);
}
+ return sandbox->UnsafeTrap(AllowRedirectedSyscall, NULL);
}
int bus_handler_fd_ = -1;
@@ -595,7 +644,7 @@ 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_C(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
@@ -618,7 +667,7 @@ BPF_TEST(SandboxBPF, SigBus, RedirectAllSyscallsPolicy) {
BPF_ASSERT(c == 0x55);
}
-BPF_TEST(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) {
+BPF_TEST_C(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
@@ -645,7 +694,7 @@ BPF_TEST(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) {
BPF_ASSERT(sigismember(&mask2, SIGUSR2));
}
-BPF_TEST(SandboxBPF, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
+BPF_TEST_C(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()
@@ -670,6 +719,8 @@ BPF_TEST(SandboxBPF, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
BPF_ASSERT(errno == 0);
}
+bool NoOpCallback() { return true; }
+
// Test a trap handler that makes use of a broker process to open().
class InitializedOpenBroker {
@@ -682,7 +733,7 @@ class InitializedOpenBroker {
broker_process_.reset(
new BrokerProcess(EPERM, allowed_files, std::vector<std::string>()));
BPF_ASSERT(broker_process() != NULL);
- BPF_ASSERT(broker_process_->Init(NULL));
+ BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback)));
initialized_ = true;
}
@@ -700,9 +751,15 @@ intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
BPF_ASSERT(aux);
BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
switch (args.nr) {
+#if defined(ANDROID)
+ case __NR_faccessat: // access is a wrapper of faccessat in android
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#else
case __NR_access:
return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
+#endif
case __NR_open:
return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
@@ -718,14 +775,19 @@ intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
}
}
-ErrorCode DenyOpenPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
- InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux);
+ErrorCode DenyOpenPolicy(SandboxBPF* sandbox,
+ int sysno,
+ InitializedOpenBroker* iob) {
if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
switch (sysno) {
+#if defined(ANDROID)
+ case __NR_faccessat:
+#else
case __NR_access:
+#endif
case __NR_open:
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
@@ -742,9 +804,9 @@ ErrorCode DenyOpenPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
BPF_TEST(SandboxBPF,
UseOpenBroker,
DenyOpenPolicy,
- InitializedOpenBroker /* BPF_AUX */) {
- BPF_ASSERT(BPF_AUX.initialized());
- BrokerProcess* broker_process = BPF_AUX.broker_process();
+ InitializedOpenBroker /* (*BPF_AUX) */) {
+ BPF_ASSERT(BPF_AUX->initialized());
+ BrokerProcess* broker_process = BPF_AUX->broker_process();
BPF_ASSERT(broker_process != NULL);
// First, use the broker "manually"
@@ -786,16 +848,35 @@ BPF_TEST(SandboxBPF,
// Simple test demonstrating how to use SandboxBPF::Cond()
-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);
- }
+class SimpleCondTestPolicy : public SandboxBPFPolicy {
+ public:
+ SimpleCondTestPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleCondTestPolicy);
+};
+
+ErrorCode SimpleCondTestPolicy::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
// We deliberately return unusual errno values upon failure, so that we
// can uniquely test for these values. In a "real" policy, you would want
// to return more traditional values.
switch (sysno) {
+#if defined(ANDROID)
+ case __NR_openat: // open is a wrapper of openat in android
+ // 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(2,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_HAS_ANY_BITS,
+ O_ACCMODE /* 0x3 */,
+ ErrorCode(EROFS),
+ ErrorCode(ErrorCode::ERR_ALLOWED));
+#else
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);
@@ -805,6 +886,7 @@ ErrorCode SimpleCondTestPolicy(SandboxBPF* sandbox, int sysno, void*) {
O_ACCMODE /* 0x3 */,
ErrorCode(EROFS),
ErrorCode(ErrorCode::ERR_ALLOWED));
+#endif
case __NR_prctl:
// Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
// disallow everything else.
@@ -824,7 +906,7 @@ ErrorCode SimpleCondTestPolicy(SandboxBPF* sandbox, int sysno, void*) {
}
}
-BPF_TEST(SandboxBPF, SimpleCondTest, SimpleCondTestPolicy) {
+BPF_TEST_C(SandboxBPF, SimpleCondTest, SimpleCondTestPolicy) {
int fd;
BPF_ASSERT((fd = open("/proc/self/comm", O_RDWR)) == -1);
BPF_ASSERT(errno == EROFS);
@@ -1122,7 +1204,7 @@ class EqualityStressTest {
// based on the system call number and the parameters that we decided
// to pass in. Verify that this condition holds true.
BPF_ASSERT(
- SandboxSyscall(
+ Syscall::Call(
sysno, args[0], args[1], args[2], args[3], args[4], args[5]) ==
-err);
}
@@ -1139,22 +1221,34 @@ class EqualityStressTest {
static const int kMaxArgs = 6;
};
-ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox, int sysno, void* aux) {
- return reinterpret_cast<EqualityStressTest*>(aux)->Policy(sandbox, sysno);
+ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox,
+ int sysno,
+ EqualityStressTest* aux) {
+ DCHECK(aux);
+ return aux->Policy(sandbox, sysno);
}
BPF_TEST(SandboxBPF,
EqualityTests,
EqualityStressTestPolicy,
- EqualityStressTest /* BPF_AUX */) {
- BPF_AUX.VerifyFilter();
+ EqualityStressTest /* (*BPF_AUX) */) {
+ BPF_AUX->VerifyFilter();
}
-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) {
+class EqualityArgumentWidthPolicy : public SandboxBPFPolicy {
+ public:
+ EqualityArgumentWidthPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EqualityArgumentWidthPolicy);
+};
+
+ErrorCode EqualityArgumentWidthPolicy::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
return sandbox->Cond(
0,
ErrorCode::TP_32BIT,
@@ -1180,24 +1274,23 @@ ErrorCode EqualityArgumentWidthPolicy(SandboxBPF* sandbox, int sysno, void*) {
0x55555555AAAAAAAAULL,
ErrorCode(1),
ErrorCode(2)));
- } else {
- return ErrorCode(ErrorCode::ERR_ALLOWED);
}
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-BPF_TEST(SandboxBPF, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
- BPF_ASSERT(SandboxSyscall(__NR_uname, 0, 0x55555555) == -1);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 0, 0xAAAAAAAA) == -2);
+BPF_TEST_C(SandboxBPF, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0, 0x55555555) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0, 0xAAAAAAAA) == -2);
#if __SIZEOF_POINTER__ > 4
// 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_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555AAAAAAAAULL) == -1);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555500000000ULL) == -2);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555511111111ULL) == -2);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x11111111AAAAAAAAULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x55555555AAAAAAAAULL) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x5555555500000000ULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x5555555511111111ULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x11111111AAAAAAAAULL) == -2);
#else
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x55555555) == -2);
#endif
}
@@ -1205,62 +1298,74 @@ 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,
- DEATH_MESSAGE("Unexpected 64bit argument detected"),
- EqualityArgumentWidthPolicy) {
- SandboxSyscall(__NR_uname, 0, 0x5555555555555555ULL);
+BPF_DEATH_TEST_C(SandboxBPF,
+ EqualityArgumentUnallowed64bit,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityArgumentWidthPolicy) {
+ Syscall::Call(__NR_uname, 0, 0x5555555555555555ULL);
}
#endif
-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));
- } else {
+class EqualityWithNegativeArgumentsPolicy : public SandboxBPFPolicy {
+ public:
+ EqualityWithNegativeArgumentsPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0xFFFFFFFF,
+ ErrorCode(1),
+ ErrorCode(2));
+ }
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-}
-BPF_TEST(SandboxBPF,
- EqualityWithNegativeArguments,
- EqualityWithNegativeArgumentsPolicy) {
- BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF) == -1);
- BPF_ASSERT(SandboxSyscall(__NR_uname, -1) == -1);
- BPF_ASSERT(SandboxSyscall(__NR_uname, -1LL) == -1);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EqualityWithNegativeArgumentsPolicy);
+};
+
+BPF_TEST_C(SandboxBPF,
+ EqualityWithNegativeArguments,
+ EqualityWithNegativeArgumentsPolicy) {
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0xFFFFFFFF) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, -1) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, -1LL) == -1);
}
#if __SIZEOF_POINTER__ > 4
-BPF_DEATH_TEST(SandboxBPF,
- EqualityWithNegative64bitArguments,
- DEATH_MESSAGE("Unexpected 64bit argument detected"),
- EqualityWithNegativeArgumentsPolicy) {
+BPF_DEATH_TEST_C(SandboxBPF,
+ EqualityWithNegative64bitArguments,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityWithNegativeArgumentsPolicy) {
// When expecting a 32bit system call argument, we look at the MSB of the
// 64bit value and allow both "0" and "-1". But the latter is allowed only
// iff the LSB was negative. So, this death test should error out.
- BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
}
#endif
-ErrorCode AllBitTestPolicy(SandboxBPF* sandbox, int sysno, void *) {
+class AllBitTestPolicy : public SandboxBPFPolicy {
+ public:
+ AllBitTestPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AllBitTestPolicy);
+};
+
+ErrorCode AllBitTestPolicy::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
// 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 (!SandboxBPF::IsValidSyscallNumber(sysno)) {
- // FIXME: we should really not have to do that in a trivial policy
- return ErrorCode(ENOSYS);
- } else if (sysno == __NR_uname) {
+ if (sysno == __NR_uname) {
return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
0x0,
@@ -1316,9 +1421,8 @@ ErrorCode AllBitTestPolicy(SandboxBPF* sandbox, int sysno, void *) {
ErrorCode(1), ErrorCode(0)),
sandbox->Kill("Invalid test case number"))))))))))));
- } else {
- return ErrorCode(ErrorCode::ERR_ALLOWED);
}
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
// Define a macro that performs tests using our test policy.
@@ -1329,10 +1433,10 @@ ErrorCode AllBitTestPolicy(SandboxBPF* sandbox, int sysno, void *) {
// to make changes to these values, you will have to edit the
// test policy instead.
#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \
- BPF_ASSERT(SandboxSyscall(__NR_uname, (testcase), (arg)) == (expected_value))
+ BPF_ASSERT(Syscall::Call(__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
+// ErrorCode(0) for failure. Syscall::Call() turns this into an
// exit code of -1 or 0.
#define EXPECT_FAILURE 0
#define EXPECT_SUCCESS -1
@@ -1343,7 +1447,7 @@ ErrorCode AllBitTestPolicy(SandboxBPF* sandbox, int sysno, void *) {
// 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) {
+BPF_TEST_C(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);
@@ -1446,17 +1550,26 @@ BPF_TEST(SandboxBPF, AllBitTests, AllBitTestPolicy) {
BITMASK_TEST(10, -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS);
}
-ErrorCode AnyBitTestPolicy(SandboxBPF* sandbox, int sysno, void*) {
+class AnyBitTestPolicy : public SandboxBPFPolicy {
+ public:
+ AnyBitTestPolicy() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnyBitTestPolicy);
+};
+
+ErrorCode AnyBitTestPolicy::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
// 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 (!SandboxBPF::IsValidSyscallNumber(sysno)) {
- // FIXME: we should really not have to do that in a trivial policy
- return ErrorCode(ENOSYS);
- } else if (sysno == __NR_uname) {
+ if (sysno == __NR_uname) {
return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
0x0,
@@ -1515,12 +1628,11 @@ ErrorCode AnyBitTestPolicy(SandboxBPF* sandbox, int sysno, void*) {
ErrorCode(1), ErrorCode(0)),
sandbox->Kill("Invalid test case number"))))))))))));
- } else {
- return ErrorCode(ErrorCode::ERR_ALLOWED);
}
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-BPF_TEST(SandboxBPF, AnyBitTests, AnyBitTestPolicy) {
+BPF_TEST_C(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);
@@ -1650,15 +1762,25 @@ intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void* aux) {
}
return -EPERM;
}
-ErrorCode PthreadPolicyEquality(SandboxBPF* sandbox, int sysno, void* aux) {
+
+class PthreadPolicyEquality : public SandboxBPFPolicy {
+ public:
+ PthreadPolicyEquality() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PthreadPolicyEquality);
+};
+
+ErrorCode PthreadPolicyEquality::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
// 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 (!SandboxBPF::IsValidSyscallNumber(sysno)) {
- // FIXME: we should really not have to do that in a trivial policy
- return ErrorCode(ENOSYS);
- } else if (sysno == __NR_clone) {
+ if (sysno == __NR_clone) {
// We have seen two different valid combinations of flags. Glibc
// 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
@@ -1685,20 +1807,28 @@ ErrorCode PthreadPolicyEquality(SandboxBPF* sandbox, int sysno, void* aux) {
kBaseAndroidCloneMask,
ErrorCode(ErrorCode::ERR_ALLOWED),
sandbox->Trap(PthreadTrapHandler, "Unknown mask"))));
- } else {
- return ErrorCode(ErrorCode::ERR_ALLOWED);
}
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-ErrorCode PthreadPolicyBitMask(SandboxBPF* sandbox, int sysno, void* aux) {
+class PthreadPolicyBitMask : public SandboxBPFPolicy {
+ public:
+ PthreadPolicyBitMask() {}
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PthreadPolicyBitMask);
+};
+
+ErrorCode PthreadPolicyBitMask::EvaluateSyscall(SandboxBPF* sandbox,
+ int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
// 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 (!SandboxBPF::IsValidSyscallNumber(sysno)) {
- // FIXME: we should really not have to do that in a trivial policy
- return ErrorCode(ENOSYS);
- } else if (sysno == __NR_clone) {
+ if (sysno == __NR_clone) {
// We have seen two different valid combinations of flags. Glibc
// 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
@@ -1730,14 +1860,13 @@ ErrorCode PthreadPolicyBitMask(SandboxBPF* sandbox, int sysno, void* aux) {
sandbox->Trap(PthreadTrapHandler,
"Missing mandatory CLONE_XXX flags "
"when creating new thread")));
- } else {
- return ErrorCode(ErrorCode::ERR_ALLOWED);
}
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
static void* ThreadFnc(void* arg) {
++*reinterpret_cast<int*>(arg);
- SandboxSyscall(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0);
+ Syscall::Call(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0);
return NULL;
}
@@ -1756,7 +1885,7 @@ 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) ==
+ while (Syscall::Call(__NR_futex, &thread_ran, FUTEX_WAIT, 0, 0, 0, 0) ==
-EINTR) {
}
BPF_ASSERT(thread_ran);
@@ -1767,16 +1896,168 @@ static void PthreadTest() {
// run-time libraries other than glibc might call __NR_fork instead of
// __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);
+ BPF_ASSERT(Syscall::Call(__NR_clone,
+ CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD,
+ 0,
+ 0,
+ &pid) == -EPERM);
}
-BPF_TEST(SandboxBPF, PthreadEquality, PthreadPolicyEquality) { PthreadTest(); }
+BPF_TEST_C(SandboxBPF, PthreadEquality, PthreadPolicyEquality) {
+ PthreadTest();
+}
+
+BPF_TEST_C(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) {
+ PthreadTest();
+}
+
+// libc might not define these even though the kernel supports it.
+#ifndef PTRACE_O_TRACESECCOMP
+#define PTRACE_O_TRACESECCOMP 0x00000080
+#endif
+
+#ifdef PTRACE_EVENT_SECCOMP
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP)
+#else
+// When Debian/Ubuntu backported seccomp-bpf support into earlier kernels, they
+// changed the value of PTRACE_EVENT_SECCOMP from 7 to 8, since 7 was taken by
+// PTRACE_EVENT_STOP (upstream chose to renumber PTRACE_EVENT_STOP to 128). If
+// PTRACE_EVENT_SECCOMP isn't defined, we have no choice but to consider both
+// values here.
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == 7 || (status >> 16) == 8)
+#endif
-BPF_TEST(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) { PthreadTest(); }
+#if defined(__arm__)
+#ifndef PTRACE_SET_SYSCALL
+#define PTRACE_SET_SYSCALL 23
+#endif
+#endif
+
+// Changes the syscall to run for a child being sandboxed using seccomp-bpf with
+// PTRACE_O_TRACESECCOMP. Should only be called when the child is stopped on
+// PTRACE_EVENT_SECCOMP.
+//
+// regs should contain the current set of registers of the child, obtained using
+// PTRACE_GETREGS.
+//
+// Depending on the architecture, this may modify regs, so the caller is
+// responsible for committing these changes using PTRACE_SETREGS.
+long SetSyscall(pid_t pid, regs_struct* regs, int syscall_number) {
+#if defined(__arm__)
+ // On ARM, the syscall is changed using PTRACE_SET_SYSCALL. We cannot use the
+ // libc ptrace call as the request parameter is an enum, and
+ // PTRACE_SET_SYSCALL may not be in the enum.
+ return syscall(__NR_ptrace, PTRACE_SET_SYSCALL, pid, NULL, syscall_number);
+#endif
+
+ SECCOMP_PT_SYSCALL(*regs) = syscall_number;
+ return 0;
+}
+
+const uint16_t kTraceData = 0xcc;
+
+class TraceAllPolicy : public SandboxBPFPolicy {
+ public:
+ TraceAllPolicy() {}
+ virtual ~TraceAllPolicy() {}
+
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
+ int system_call_number) const OVERRIDE {
+ return ErrorCode(ErrorCode::ERR_TRACE + kTraceData);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TraceAllPolicy);
+};
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) {
+ if (SandboxBPF::SupportsSeccompSandbox(-1) !=
+ sandbox::SandboxBPF::STATUS_AVAILABLE) {
+ return;
+ }
+
+#if defined(__arm__)
+ printf("This test is currently disabled on ARM due to a kernel bug.");
+ return;
+#endif
+
+ pid_t pid = fork();
+ BPF_ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ pid_t my_pid = getpid();
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_TRACEME, -1, NULL, NULL));
+ BPF_ASSERT_EQ(0, raise(SIGSTOP));
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new TraceAllPolicy);
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ // getpid is allowed.
+ BPF_ASSERT_EQ(my_pid, syscall(__NR_getpid));
+
+ // write to stdout is skipped and returns a fake value.
+ BPF_ASSERT_EQ(kExpectedReturnValue,
+ syscall(__NR_write, STDOUT_FILENO, "A", 1));
+
+ // kill is rewritten to exit(kExpectedReturnValue).
+ syscall(__NR_kill, my_pid, SIGKILL);
+
+ // Should not be reached.
+ BPF_ASSERT(false);
+ }
+
+ int status;
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, WUNTRACED)) != -1);
+ BPF_ASSERT(WIFSTOPPED(status));
+
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETOPTIONS, pid, NULL,
+ reinterpret_cast<void*>(PTRACE_O_TRACESECCOMP)));
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ while (true) {
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, 0)) != -1);
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ BPF_ASSERT(WIFEXITED(status));
+ BPF_ASSERT_EQ(kExpectedReturnValue, WEXITSTATUS(status));
+ break;
+ }
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
+ !IS_SECCOMP_EVENT(status)) {
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ continue;
+ }
+
+ unsigned long data;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data));
+ BPF_ASSERT_EQ(kTraceData, data);
+
+ regs_struct regs;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_GETREGS, pid, NULL, &regs));
+ switch (SECCOMP_PT_SYSCALL(regs)) {
+ case __NR_write:
+ // Skip writes to stdout, make it return kExpectedReturnValue. Allow
+ // writes to stderr so that BPF_ASSERT messages show up.
+ if (SECCOMP_PT_PARM1(regs) == STDOUT_FILENO) {
+ BPF_ASSERT_NE(-1, SetSyscall(pid, &regs, -1));
+ SECCOMP_PT_RESULT(regs) = kExpectedReturnValue;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, &regs));
+ }
+ break;
+
+ case __NR_kill:
+ // Rewrite to exit(kExpectedReturnValue).
+ BPF_ASSERT_NE(-1, SetSyscall(pid, &regs, __NR_exit));
+ SECCOMP_PT_PARM1(regs) = kExpectedReturnValue;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, &regs));
+ break;
+
+ default:
+ // Allow all other syscalls.
+ break;
+ }
+
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ }
+}
} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.cc b/chromium/sandbox/linux/seccomp-bpf/syscall.cc
index acf207dc14c..64c0b8eb9b4 100644
--- a/chromium/sandbox/linux/seccomp-bpf/syscall.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall.cc
@@ -11,169 +11,177 @@
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
- // inline code, which would inadvertently end up duplicating
- // the entry point.
- // "gcc" can suppress code duplication with suitable function
- // attributes, but "clang" doesn't have this ability.
- // The "clang" developer mailing list suggested that the correct
- // and portable solution is a file-scope assembly block.
- // N.B. We do mark our code as a proper function so that backtraces
- // work correctly. But we make absolutely no attempt to use the
- // ABI's calling conventions for passing arguments. We will only
- // ever be called from assembly code and thus can pick more
- // suitable calling conventions.
+namespace {
+
+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
+ // inline code, which would inadvertently end up duplicating
+ // the entry point.
+ // "gcc" can suppress code duplication with suitable function
+ // attributes, but "clang" doesn't have this ability.
+ // The "clang" developer mailing list suggested that the correct
+ // and portable solution is a file-scope assembly block.
+ // N.B. We do mark our code as a proper function so that backtraces
+ // work correctly. But we make absolutely no attempt to use the
+ // ABI's calling conventions for passing arguments. We will only
+ // ever be called from assembly code and thus can pick more
+ // suitable calling conventions.
#if defined(__i386__)
- ".text\n"
- ".align 16, 0x90\n"
- ".type SyscallAsm, @function\n"
- "SyscallAsm:.cfi_startproc\n"
- // Check if "%eax" is negative. If so, do not attempt to make a
- // system call. Instead, compute the return address that is visible
- // to the kernel after we execute "int $0x80". This address can be
- // used as a marker that BPF code inspects.
- "test %eax, %eax\n"
- "jge 1f\n"
- // Always, make sure that our code is position-independent, or
- // address space randomization might not work on i386. This means,
- // we can't use "lea", but instead have to rely on "call/pop".
- "call 0f; .cfi_adjust_cfa_offset 4\n"
- "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
- "addl $2f-0b, %eax\n"
- "ret\n"
- // Save register that we don't want to clobber. On i386, we need to
- // save relatively aggressively, as there are a couple or registers
- // that are used internally (e.g. %ebx for position-independent
- // code, and %ebp for the frame pointer), and as we need to keep at
- // least a few registers available for the register allocator.
- "1:push %esi; .cfi_adjust_cfa_offset 4\n"
- "push %edi; .cfi_adjust_cfa_offset 4\n"
- "push %ebx; .cfi_adjust_cfa_offset 4\n"
- "push %ebp; .cfi_adjust_cfa_offset 4\n"
- // Copy entries from the array holding the arguments into the
- // correct CPU registers.
- "movl 0(%edi), %ebx\n"
- "movl 4(%edi), %ecx\n"
- "movl 8(%edi), %edx\n"
- "movl 12(%edi), %esi\n"
- "movl 20(%edi), %ebp\n"
- "movl 16(%edi), %edi\n"
- // Enter the kernel.
- "int $0x80\n"
- // This is our "magic" return address that the BPF filter sees.
- "2:"
- // Restore any clobbered registers that we didn't declare to the
- // compiler.
- "pop %ebp; .cfi_adjust_cfa_offset -4\n"
- "pop %ebx; .cfi_adjust_cfa_offset -4\n"
- "pop %edi; .cfi_adjust_cfa_offset -4\n"
- "pop %esi; .cfi_adjust_cfa_offset -4\n"
- "ret\n"
- ".cfi_endproc\n"
- "9:.size SyscallAsm, 9b-SyscallAsm\n"
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%eax" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "int $0x80". This address can be
+ // used as a marker that BPF code inspects.
+ "test %eax, %eax\n"
+ "jge 1f\n"
+ // Always, make sure that our code is position-independent, or
+ // address space randomization might not work on i386. This means,
+ // we can't use "lea", but instead have to rely on "call/pop".
+ "call 0f; .cfi_adjust_cfa_offset 4\n"
+ "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
+ "addl $2f-0b, %eax\n"
+ "ret\n"
+ // Save register that we don't want to clobber. On i386, we need to
+ // save relatively aggressively, as there are a couple or registers
+ // that are used internally (e.g. %ebx for position-independent
+ // code, and %ebp for the frame pointer), and as we need to keep at
+ // least a few registers available for the register allocator.
+ "1:push %esi; .cfi_adjust_cfa_offset 4\n"
+ "push %edi; .cfi_adjust_cfa_offset 4\n"
+ "push %ebx; .cfi_adjust_cfa_offset 4\n"
+ "push %ebp; .cfi_adjust_cfa_offset 4\n"
+ // Copy entries from the array holding the arguments into the
+ // correct CPU registers.
+ "movl 0(%edi), %ebx\n"
+ "movl 4(%edi), %ecx\n"
+ "movl 8(%edi), %edx\n"
+ "movl 12(%edi), %esi\n"
+ "movl 20(%edi), %ebp\n"
+ "movl 16(%edi), %edi\n"
+ // Enter the kernel.
+ "int $0x80\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:"
+ // Restore any clobbered registers that we didn't declare to the
+ // compiler.
+ "pop %ebp; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebx; .cfi_adjust_cfa_offset -4\n"
+ "pop %edi; .cfi_adjust_cfa_offset -4\n"
+ "pop %esi; .cfi_adjust_cfa_offset -4\n"
+ "ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
#elif defined(__x86_64__)
- ".text\n"
- ".align 16, 0x90\n"
- ".type SyscallAsm, @function\n"
- "SyscallAsm:.cfi_startproc\n"
- // Check if "%rax" is negative. If so, do not attempt to make a
- // system call. Instead, compute the return address that is visible
- // to the kernel after we execute "syscall". This address can be
- // used as a marker that BPF code inspects.
- "test %rax, %rax\n"
- "jge 1f\n"
- // Always make sure that our code is position-independent, or the
- // linker will throw a hissy fit on x86-64.
- "call 0f; .cfi_adjust_cfa_offset 8\n"
- "0:pop %rax; .cfi_adjust_cfa_offset -8\n"
- "addq $2f-0b, %rax\n"
- "ret\n"
- // We declared all clobbered registers to the compiler. On x86-64,
- // there really isn't much of a problem with register pressure. So,
- // we can go ahead and directly copy the entries from the arguments
- // array into the appropriate CPU registers.
- "1:movq 0(%r12), %rdi\n"
- "movq 8(%r12), %rsi\n"
- "movq 16(%r12), %rdx\n"
- "movq 24(%r12), %r10\n"
- "movq 32(%r12), %r8\n"
- "movq 40(%r12), %r9\n"
- // Enter the kernel.
- "syscall\n"
- // This is our "magic" return address that the BPF filter sees.
- "2:ret\n"
- ".cfi_endproc\n"
- "9:.size SyscallAsm, 9b-SyscallAsm\n"
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%rax" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "test %rax, %rax\n"
+ "jge 1f\n"
+ // Always make sure that our code is position-independent, or the
+ // linker will throw a hissy fit on x86-64.
+ "call 0f; .cfi_adjust_cfa_offset 8\n"
+ "0:pop %rax; .cfi_adjust_cfa_offset -8\n"
+ "addq $2f-0b, %rax\n"
+ "ret\n"
+ // We declared all clobbered registers to the compiler. On x86-64,
+ // there really isn't much of a problem with register pressure. So,
+ // we can go ahead and directly copy the entries from the arguments
+ // array into the appropriate CPU registers.
+ "1:movq 0(%r12), %rdi\n"
+ "movq 8(%r12), %rsi\n"
+ "movq 16(%r12), %rdx\n"
+ "movq 24(%r12), %r10\n"
+ "movq 32(%r12), %r8\n"
+ "movq 40(%r12), %r9\n"
+ // Enter the kernel.
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
#elif defined(__arm__)
- // Throughout this file, we use the same mode (ARM vs. thumb)
- // that the C++ compiler uses. This means, when transfering control
- // from C++ to assembly code, we do not need to switch modes (e.g.
- // by using the "bx" instruction). It also means that our assembly
- // code should not be invoked directly from code that lives in
- // other compilation units, as we don't bother implementing thumb
- // interworking. That's OK, as we don't make any of the assembly
- // symbols public. They are all local to this file.
- ".text\n"
- ".align 2\n"
- ".type SyscallAsm, %function\n"
+ // Throughout this file, we use the same mode (ARM vs. thumb)
+ // that the C++ compiler uses. This means, when transfering control
+ // from C++ to assembly code, we do not need to switch modes (e.g.
+ // by using the "bx" instruction). It also means that our assembly
+ // code should not be invoked directly from code that lives in
+ // other compilation units, as we don't bother implementing thumb
+ // interworking. That's OK, as we don't make any of the assembly
+ // symbols public. They are all local to this file.
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
#if defined(__thumb__)
- ".thumb_func\n"
+ ".thumb_func\n"
#else
- ".arm\n"
+ ".arm\n"
#endif
- "SyscallAsm:.fnstart\n"
- "@ args = 0, pretend = 0, frame = 8\n"
- "@ frame_needed = 1, uses_anonymous_args = 0\n"
+ "SyscallAsm:.fnstart\n"
+ "@ args = 0, pretend = 0, frame = 8\n"
+ "@ frame_needed = 1, uses_anonymous_args = 0\n"
#if defined(__thumb__)
- ".cfi_startproc\n"
- "push {r7, lr}\n"
- ".cfi_offset 14, -4\n"
- ".cfi_offset 7, -8\n"
- "mov r7, sp\n"
- ".cfi_def_cfa_register 7\n"
- ".cfi_def_cfa_offset 8\n"
+ ".cfi_startproc\n"
+ "push {r7, lr}\n"
+ ".cfi_offset 14, -4\n"
+ ".cfi_offset 7, -8\n"
+ "mov r7, sp\n"
+ ".cfi_def_cfa_register 7\n"
+ ".cfi_def_cfa_offset 8\n"
#else
- "stmfd sp!, {fp, lr}\n"
- "add fp, sp, #4\n"
+ "stmfd sp!, {fp, lr}\n"
+ "add fp, sp, #4\n"
#endif
- // Check if "r0" is negative. If so, do not attempt to make a
- // system call. Instead, compute the return address that is visible
- // to the kernel after we execute "swi 0". This address can be
- // used as a marker that BPF code inspects.
- "cmp r0, #0\n"
- "bge 1f\n"
- "ldr r0, =2f\n"
- "b 2f\n"
- // We declared (almost) all clobbered registers to the compiler. On
- // ARM there is no particular register pressure. So, we can go
- // ahead and directly copy the entries from the arguments array
- // into the appropriate CPU registers.
- "1:ldr r5, [r6, #20]\n"
- "ldr r4, [r6, #16]\n"
- "ldr r3, [r6, #12]\n"
- "ldr r2, [r6, #8]\n"
- "ldr r1, [r6, #4]\n"
- "mov r7, r0\n"
- "ldr r0, [r6, #0]\n"
- // Enter the kernel
- "swi 0\n"
- // Restore the frame pointer. Also restore the program counter from
- // the link register; this makes us return to the caller.
+ // Check if "r0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "swi 0". This address can be
+ // used as a marker that BPF code inspects.
+ "cmp r0, #0\n"
+ "bge 1f\n"
+ "adr r0, 2f\n"
+ "b 2f\n"
+ // We declared (almost) all clobbered registers to the compiler. On
+ // ARM there is no particular register pressure. So, we can go
+ // ahead and directly copy the entries from the arguments array
+ // into the appropriate CPU registers.
+ "1:ldr r5, [r6, #20]\n"
+ "ldr r4, [r6, #16]\n"
+ "ldr r3, [r6, #12]\n"
+ "ldr r2, [r6, #8]\n"
+ "ldr r1, [r6, #4]\n"
+ "mov r7, r0\n"
+ "ldr r0, [r6, #0]\n"
+ // Enter the kernel
+ "swi 0\n"
+// Restore the frame pointer. Also restore the program counter from
+// the link register; this makes us return to the caller.
#if defined(__thumb__)
- "2:pop {r7, pc}\n"
- ".cfi_endproc\n"
+ "2:pop {r7, pc}\n"
+ ".cfi_endproc\n"
#else
- "2:ldmfd sp!, {fp, pc}\n"
+ "2:ldmfd sp!, {fp, pc}\n"
#endif
- ".fnend\n"
- "9:.size SyscallAsm, 9b-SyscallAsm\n"
+ ".fnend\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
#endif
- ); // asm
+ ); // asm
+
+} // namespace
-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 Syscall::Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5) {
// We rely on "intptr_t" to be the exact size as a "void *". This is
// typically true, but just in case, we add a check. The language
// specification allows platforms some leeway in cases, where
@@ -181,61 +189,78 @@ intptr_t SandboxSyscall(int nr,
// that this would only be an issue for IA64, which we are currently not
// planning on supporting. And it is even possible that this would work
// on IA64, but for lack of actual hardware, I cannot test.
- COMPILE_ASSERT(sizeof(void *) == sizeof(intptr_t),
+ COMPILE_ASSERT(sizeof(void*) == sizeof(intptr_t),
pointer_types_and_intptr_must_be_exactly_the_same_size);
- const intptr_t args[6] = { p0, p1, p2, p3, p4, p5 };
+ const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
- // Invoke our file-scope assembly code. The constraints have been picked
- // carefully to match what the rest of the assembly code expects in input,
- // output, and clobbered registers.
+// Invoke our file-scope assembly code. The constraints have been picked
+// carefully to match what the rest of the assembly code expects in input,
+// output, and clobbered registers.
#if defined(__i386__)
intptr_t ret = nr;
asm volatile(
- "call SyscallAsm\n"
- // N.B. These are not the calling conventions normally used by the ABI.
- : "=a"(ret)
- : "0"(ret), "D"(args)
- : "cc", "esp", "memory", "ecx", "edx");
+ "call SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=a"(ret)
+ : "0"(ret), "D"(args)
+ : "cc", "esp", "memory", "ecx", "edx");
#elif defined(__x86_64__)
intptr_t ret = nr;
{
- register const intptr_t *data __asm__("r12") = args;
+ register const intptr_t* data __asm__("r12") = args;
asm volatile(
- "lea -128(%%rsp), %%rsp\n" // Avoid red zone.
- "call SyscallAsm\n"
- "lea 128(%%rsp), %%rsp\n"
- // N.B. These are not the calling conventions normally used by the ABI.
- : "=a"(ret)
- : "0"(ret), "r"(data)
- : "cc", "rsp", "memory",
- "rcx", "rdi", "rsi", "rdx", "r8", "r9", "r10", "r11");
+ "lea -128(%%rsp), %%rsp\n" // Avoid red zone.
+ "call SyscallAsm\n"
+ "lea 128(%%rsp), %%rsp\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=a"(ret)
+ : "0"(ret), "r"(data)
+ : "cc",
+ "rsp",
+ "memory",
+ "rcx",
+ "rdi",
+ "rsi",
+ "rdx",
+ "r8",
+ "r9",
+ "r10",
+ "r11");
}
#elif defined(__arm__)
intptr_t ret;
{
register intptr_t inout __asm__("r0") = nr;
- register const intptr_t *data __asm__("r6") = args;
+ register const intptr_t* data __asm__("r6") = args;
asm volatile(
- "bl SyscallAsm\n"
- // N.B. These are not the calling conventions normally used by the ABI.
- : "=r"(inout)
- : "0"(inout), "r"(data)
- : "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.
- // In ARM mode, we have a dedicated frame pointer register and "r7" is
- // thus available as a general purpose register. We don't preserve it,
- // but instead mark it as clobbered.
- , "r7"
-#endif
- );
+ "bl SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "cc",
+ "lr",
+ "memory",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5"
+#if !defined(__thumb__)
+ // 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.
+ // In ARM mode, we have a dedicated frame pointer register and "r7" is
+ // thus available as a general purpose register. We don't preserve it,
+ // but instead mark it as clobbered.
+ ,
+ "r7"
+#endif // !defined(__thumb__)
+ );
ret = inout;
}
#else
- errno = ENOSYS;
- intptr_t ret = -1;
+#error "Unimplemented architecture"
#endif
return ret;
}
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.h b/chromium/sandbox/linux/seccomp-bpf/syscall.h
index 0b51380e88b..57970a35b04 100644
--- a/chromium/sandbox/linux/seccomp-bpf/syscall.h
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall.h
@@ -7,131 +7,82 @@
#include <stdint.h>
-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
-// works by writing a little bit of asm() code that a) enters the kernel, and
-// that also b) can be invoked in a way that computes this return address.
-// 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);
-
-// System calls can take up to six parameters. Traditionally, glibc
-// implements this property by using variadic argument lists. This works, but
-// confuses modern tools such as valgrind, because we are nominally passing
-// uninitialized data whenever we call through this function and pass less
-// than the full six arguments.
-// So, instead, we use C++'s template system to achieve a very similar
-// effect. C++ automatically sets the unused parameters to zero for us, and
-// it also does the correct type expansion (e.g. from 32bit to 64bit) where
-// necessary.
-// We have to use C-style cast operators as we want to be able to accept both
-// integer and pointer types.
-// We explicitly mark all functions as inline. This is not necessary in
-// optimized builds, where the compiler automatically figures out that it
-// can inline everything. But it makes stack traces of unoptimized builds
-// 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>
-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);
-}
-
-#else // Pre-C++11
-
-// TODO(markus): C++11 has a much more concise and readable solution for
-// expressing what we are doing here. Delete the fall-back code for older
-// compilers as soon as we have fully switched to C++11
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
-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);
-}
-
-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>
-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>
-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>
-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>
-inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2)
- __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>
-inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1)
- __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>
-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) {
- return SandboxSyscall(nr, 0, 0, 0, 0, 0, 0);
-}
+namespace sandbox {
-#endif // Pre-C++11
+// This purely static class can be used to perform system calls with some
+// low-level control.
+class SANDBOX_EXPORT Syscall {
+ public:
+ // This performs system call |nr| with the arguments p0 to p5 from a constant
+ // userland address, which is for instance observable by seccomp-bpf filters.
+ // The constant userland address from which these system calls are made will
+ // be returned if |nr| is passed as -1.
+ // On error, this function will return a value between -1 and -4095 which
+ // should be interpreted as -errno.
+ static intptr_t Call(int nr,
+ 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
+ // confuses modern tools such as valgrind, because we are nominally passing
+ // uninitialized data whenever we call through this function and pass less
+ // than the full six arguments.
+ // So, instead, we use C++'s template system to achieve a very similar
+ // effect. C++ automatically sets the unused parameters to zero for us, and
+ // it also does the correct type expansion (e.g. from 32bit to 64bit) where
+ // necessary.
+ // We have to use C-style cast operators as we want to be able to accept both
+ // integer and pointer types.
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+ return Call(nr,
+ (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>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
+ return Call(nr, p0, p1, p2, p3, p4, 0);
+ }
+
+ template <class T0, class T1, class T2, class T3>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
+ return Call(nr, p0, p1, p2, p3, 0, 0);
+ }
+
+ template <class T0, class T1, class T2>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2) {
+ return Call(nr, p0, p1, p2, 0, 0, 0);
+ }
+
+ template <class T0, class T1>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1) {
+ return Call(nr, p0, p1, 0, 0, 0, 0);
+ }
+
+ template <class T0>
+ static inline intptr_t Call(int nr, T0 p0) {
+ return Call(nr, p0, 0, 0, 0, 0, 0);
+ }
+
+ static inline intptr_t Call(int nr) { return Call(nr, 0, 0, 0, 0, 0, 0); }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Syscall);
+};
} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h
index 3b56ea3144d..7842b2a85b2 100644
--- a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include "base/basictypes.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -31,7 +32,7 @@ namespace sandbox {
// }
//
// TODO(markus): Make this a classic C++ iterator.
-class SyscallIterator {
+class SANDBOX_EXPORT SyscallIterator {
public:
explicit SyscallIterator(bool invalid_only)
: invalid_only_(invalid_only), done_(false), num_(0) {}
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
index 60db69bcd6b..80b5079bae0 100644
--- a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -12,6 +12,7 @@
#include "base/basictypes.h"
#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
@@ -31,24 +32,25 @@ const int kMMapNr = __NR_mmap;
#endif
TEST(Syscall, WellKnownEntryPoint) {
-// Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM,
+// Test that Syscall::Call(-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));
+ EXPECT_NE(Syscall::Call(-1), syscall(-1));
#endif
-// If possible, test that SandboxSyscall(-1) returns the address right after
+// If possible, test that Syscall::Call(-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*)Syscall::Call(-1))[-1]); // INT 0x80
#elif defined(__x86_64__)
- EXPECT_EQ(0x050Fu, ((uint16_t*)SandboxSyscall(-1))[-1]); // SYSCALL
+ EXPECT_EQ(0x050Fu, ((uint16_t*)Syscall::Call(-1))[-1]); // SYSCALL
#elif defined(__arm__)
#if defined(__thumb__)
- EXPECT_EQ(0xDF00u, ((uint16_t*)SandboxSyscall(-1))[-1]); // SWI 0
+ EXPECT_EQ(0xDF00u, ((uint16_t*)Syscall::Call(-1))[-1]); // SWI 0
#else
- EXPECT_EQ(0xEF000000u, ((uint32_t*)SandboxSyscall(-1))[-1]); // SVC 0
+ EXPECT_EQ(0xEF000000u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
#endif
#else
#warning Incomplete test case; need port for target platform
@@ -57,20 +59,28 @@ TEST(Syscall, WellKnownEntryPoint) {
TEST(Syscall, TrivialSyscallNoArgs) {
// Test that we can do basic system calls
- EXPECT_EQ(SandboxSyscall(__NR_getpid), syscall(__NR_getpid));
+ EXPECT_EQ(Syscall::Call(__NR_getpid), syscall(__NR_getpid));
}
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 = IGNORE_EINTR(SandboxSyscall(__NR_close, new_fd));
+ ASSERT_GE(new_fd = Syscall::Call(__NR_dup, 2), 0);
+ int close_return_value = IGNORE_EINTR(Syscall::Call(__NR_close, new_fd));
ASSERT_EQ(close_return_value, 0);
}
+TEST(Syscall, TrivialFailingSyscall) {
+ errno = -42;
+ int ret = Syscall::Call(__NR_dup, -1);
+ ASSERT_EQ(-EBADF, ret);
+ // Verify that Syscall::Call does not touch errno.
+ ASSERT_EQ(-42, errno);
+}
+
// SIGSYS trap handler that will be called on __NR_uname.
intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
- // |aux| is a pointer to our BPF_AUX.
+ // |aux| is our BPF_AUX pointer.
std::vector<uint64_t>* const seen_syscall_args =
static_cast<std::vector<uint64_t>*>(aux);
BPF_ASSERT(arraysize(args.args) == 6);
@@ -78,7 +88,9 @@ intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
return -ENOMEM;
}
-ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, int sysno, void* aux) {
+ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox,
+ int sysno,
+ std::vector<uint64_t>* aux) {
if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
@@ -89,12 +101,13 @@ ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, int sysno, void* aux) {
}
}
-// We are testing SandboxSyscall() by making use of a BPF filter that allows us
+// We are testing Syscall::Call() 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,
- std::vector<uint64_t> /* BPF_AUX */) {
+ 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
// additional tests to try other types. What we will see depends on
@@ -107,93 +120,93 @@ BPF_TEST(Syscall,
// 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(Syscall::Call(__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);
+ BPF_ASSERT(BPF_AUX->size() == 6);
// Don't loop here so that we can see which argument does cause the failure
// easily from the failing line.
// uint64_t is the type passed to our SIGSYS handler.
- BPF_ASSERT(BPF_AUX[0] == static_cast<uint64_t>(syscall_args[0]));
- BPF_ASSERT(BPF_AUX[1] == static_cast<uint64_t>(syscall_args[1]));
- BPF_ASSERT(BPF_AUX[2] == static_cast<uint64_t>(syscall_args[2]));
- BPF_ASSERT(BPF_AUX[3] == static_cast<uint64_t>(syscall_args[3]));
- BPF_ASSERT(BPF_AUX[4] == static_cast<uint64_t>(syscall_args[4]));
- BPF_ASSERT(BPF_AUX[5] == static_cast<uint64_t>(syscall_args[5]));
+ BPF_ASSERT((*BPF_AUX)[0] == static_cast<uint64_t>(syscall_args[0]));
+ BPF_ASSERT((*BPF_AUX)[1] == static_cast<uint64_t>(syscall_args[1]));
+ BPF_ASSERT((*BPF_AUX)[2] == static_cast<uint64_t>(syscall_args[2]));
+ BPF_ASSERT((*BPF_AUX)[3] == static_cast<uint64_t>(syscall_args[3]));
+ BPF_ASSERT((*BPF_AUX)[4] == static_cast<uint64_t>(syscall_args[4]));
+ BPF_ASSERT((*BPF_AUX)[5] == static_cast<uint64_t>(syscall_args[5]));
}
TEST(Syscall, ComplexSyscallSixArgs) {
int fd;
- ASSERT_LE(0, fd = SandboxSyscall(__NR_open, "/dev/null", O_RDWR, 0L));
+ ASSERT_LE(0, fd = Syscall::Call(__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)));
+ ASSERT_NE(
+ (char*)NULL,
+ addr0 = reinterpret_cast<char*>(Syscall::Call(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;
ASSERT_EQ(addr0,
addr1 = reinterpret_cast<char*>(
- SandboxSyscall(kMMapNr,
- addr0,
- 4096L,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
- fd,
- 0L)));
+ Syscall::Call(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, IGNORE_EINTR(SandboxSyscall(__NR_close, fd)));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__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);
+ ASSERT_GE(fd = Syscall::Call(__NR_open, "/proc/self/exe", O_RDONLY, 0L), 0);
char* addr2, *addr3;
ASSERT_NE((char*)NULL,
- addr2 = reinterpret_cast<char*>(SandboxSyscall(
+ addr2 = reinterpret_cast<char*>(Syscall::Call(
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,
+ addr3 = reinterpret_cast<char*>(Syscall::Call(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
// contents matches what we are getting from a read() operation.
char buf[8192];
- EXPECT_EQ(8192, SandboxSyscall(__NR_read, fd, buf, 8192L));
+ EXPECT_EQ(8192, Syscall::Call(__NR_read, fd, buf, 8192L));
EXPECT_EQ(0, memcmp(addr2, buf, 8192));
// Clean up
- EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr2, 8192L));
- EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr3, 4096L));
- EXPECT_EQ(0, IGNORE_EINTR(SandboxSyscall(__NR_close, fd)));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 8192L));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
}
} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.cc b/chromium/sandbox/linux/seccomp-bpf/trap.cc
index 553a9043bfb..4c42111c2b0 100644
--- a/chromium/sandbox/linux/seccomp-bpf/trap.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/trap.cc
@@ -82,8 +82,11 @@ Trap::Trap()
}
if (!IsDefaultSignalAction(old_sa)) {
- // TODO(jln): make this FATAL, at least in DEBUG mode.
- LOG(ERROR) << "Existing signal handler when trying to install SIGSYS";
+ static const char kExistingSIGSYSMsg[] =
+ "Existing signal handler when trying to install SIGSYS. SIGSYS needs "
+ "to be reserved for seccomp-bpf.";
+ DLOG(FATAL) << kExistingSIGSYSMsg;
+ LOG(ERROR) << kExistingSIGSYSMsg;
}
// Unmask SIGSYS
@@ -165,13 +168,13 @@ void Trap::SigSys(int nr, siginfo_t* info, void* void_context) {
if (sigsys.nr == __NR_clone) {
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));
+ rc = Syscall::Call(sigsys.nr,
+ 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_) {
@@ -224,7 +227,7 @@ ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe) {
// we never return an ErrorCode that is marked as "unsafe". This also
// means, the BPF compiler will never emit code that allow unsafe system
// calls to by-pass the filter (because they use the magic return address
- // from SandboxSyscall(-1)).
+ // from Syscall::Call(-1)).
// This SANDBOX_DIE() can optionally be removed. It won't break security,
// but it might make error messages from the BPF compiler a little harder
diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.h b/chromium/sandbox/linux/seccomp-bpf/trap.h
index 334a30d965a..adc6d7f5e22 100644
--- a/chromium/sandbox/linux/seccomp-bpf/trap.h
+++ b/chromium/sandbox/linux/seccomp-bpf/trap.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -25,7 +26,7 @@ class ErrorCode;
// Preferably, that means that no other threads should be running at that
// time. For the purposes of our sandbox, this assertion should always be
// true. Threads are incompatible with the seccomp sandbox anyway.
-class Trap {
+class SANDBOX_EXPORT Trap {
public:
// TrapFnc is a pointer to a function that handles Seccomp traps in
// user-space. The seccomp policy can request that a trap handler gets
@@ -62,10 +63,6 @@ class Trap {
static ErrorCode ErrorCodeFromTrapId(uint16_t id);
private:
- // The destructor is unimplemented. Don't ever attempt to destruct this
- // object. It'll break subsequent system calls that trigger a SIGSYS.
- ~Trap();
-
struct TrapKey {
TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {}
TrapFnc fnc;
@@ -75,6 +72,14 @@ class Trap {
};
typedef std::map<TrapKey, uint16_t> TrapIds;
+ // Our constructor is private. A shared global instance is created
+ // automatically as needed.
+ Trap();
+
+ // The destructor is unimplemented. Don't ever attempt to destruct this
+ // object. It'll break subsequent system calls that trigger a SIGSYS.
+ ~Trap();
+
// We only have a very small number of methods. We opt to make them static
// and have them internally call GetInstance(). This is a little more
// convenient than having each caller obtain short-lived reference to the
@@ -104,11 +109,9 @@ class Trap {
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.
// Copying and assigning is unimplemented. It doesn't make sense for a
// singleton.
- DISALLOW_IMPLICIT_CONSTRUCTORS(Trap);
+ DISALLOW_COPY_AND_ASSIGN(Trap);
};
} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf/verifier.cc b/chromium/sandbox/linux/seccomp-bpf/verifier.cc
index 1292504decc..0863556e080 100644
--- a/chromium/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/verifier.cc
@@ -387,7 +387,9 @@ bool Verifier::VerifyBPF(SandboxBPF* sandbox,
}
#endif
#endif
- ErrorCode code = policy.EvaluateSyscall(sandbox, sysnum);
+ ErrorCode code = iter.IsValid(sysnum)
+ ? policy.EvaluateSyscall(sandbox, sysnum)
+ : policy.InvalidSyscall(sandbox);
if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) {
return false;
}
@@ -421,10 +423,10 @@ uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
switch (r & SECCOMP_RET_ACTION) {
case SECCOMP_RET_TRAP:
case SECCOMP_RET_ERRNO:
+ case SECCOMP_RET_TRACE:
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";
diff --git a/chromium/sandbox/linux/services/android_futex.h b/chromium/sandbox/linux/services/android_futex.h
new file mode 100644
index 00000000000..032b02481a1
--- /dev/null
+++ b/chromium/sandbox/linux/services/android_futex.h
@@ -0,0 +1,28 @@
+// 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_ANDROID_FUTEX_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_
+
+#if !defined(FUTEX_PRIVATE_FLAG)
+#define FUTEX_PRIVATE_FLAG 128
+#endif
+
+#if !defined(FUTEX_CLOCK_REALTIME)
+#define FUTEX_CLOCK_REALTIME 256
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI)
+#define FUTEX_CMP_REQUEUE_PI 12
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI_PRIVATE)
+#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#if !defined(FUTEX_CMD_MASK)
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_
diff --git a/chromium/sandbox/linux/services/android_ucontext.h b/chromium/sandbox/linux/services/android_ucontext.h
index 437bbab7bab..caabaf5b1b3 100644
--- a/chromium/sandbox/linux/services/android_ucontext.h
+++ b/chromium/sandbox/linux/services/android_ucontext.h
@@ -11,6 +11,8 @@
#include "sandbox/linux/services/android_arm_ucontext.h"
#elif defined(__i386__)
#include "sandbox/linux/services/android_i386_ucontext.h"
+#elif defined(__x86_64__)
+#include "sandbox/linux/services/android_x86_64_ucontext.h"
#else
#error "No support for your architecture in Android header"
#endif
diff --git a/chromium/sandbox/linux/services/android_x86_64_ucontext.h b/chromium/sandbox/linux/services/android_x86_64_ucontext.h
new file mode 100644
index 00000000000..ef328e55d6f
--- /dev/null
+++ b/chromium/sandbox/linux/services/android_x86_64_ucontext.h
@@ -0,0 +1,88 @@
+// 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_ANDROID_X86_64_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_
+
+// We do something compatible with glibc. Hopefully, at some point Android will
+// provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined.
+// Spec:
+// http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/libc-ddefs.html#AEN5668
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+struct _libc_fpxreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+ unsigned short padding[3];
+};
+
+struct _libc_xmmreg {
+ uint32_t element[4];
+};
+
+struct _libc_fpstate {
+ uint16_t cwd;
+ uint16_t swd;
+ uint16_t twd;
+ uint16_t fop;
+ uint64_t rip;
+ uint64_t rdp;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ struct _libc_fpxreg _st[8];
+ struct _libc_xmmreg _xmm[16];
+ uint32_t padding[24];
+};
+
+typedef uint64_t greg_t;
+
+typedef struct {
+ greg_t gregs[23];
+ struct _libc_fpstate* fpregs;
+ unsigned long __reserved1[8];
+} mcontext_t;
+
+enum {
+ REG_R8 = 0,
+ REG_R9,
+ REG_R10,
+ REG_R11,
+ REG_R12,
+ REG_R13,
+ REG_R14,
+ REG_R15,
+ REG_RDI,
+ REG_RSI,
+ REG_RBP,
+ REG_RBX,
+ REG_RDX,
+ REG_RAX,
+ REG_RCX,
+ REG_RSP,
+ REG_RIP,
+ REG_EFL,
+ REG_CSGSFS,
+ REG_ERR,
+ REG_TRAPNO,
+ REG_OLDMASK,
+ REG_CR2,
+ NGREG,
+};
+
+typedef struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_
diff --git a/chromium/sandbox/linux/services/broker_process.cc b/chromium/sandbox/linux/services/broker_process.cc
index 438e9726374..ef916f223a8 100644
--- a/chromium/sandbox/linux/services/broker_process.cc
+++ b/chromium/sandbox/linux/services/broker_process.cc
@@ -5,10 +5,12 @@
#include "sandbox/linux/services/broker_process.h"
#include <fcntl.h>
+#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
@@ -16,12 +18,16 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/memory/scoped_vector.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 "base/third_party/valgrind/valgrind.h"
#include "build/build_config.h"
#include "sandbox/linux/services/linux_syscalls.h"
@@ -31,6 +37,22 @@
namespace {
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+// A little open(2) wrapper to handle some oddities for us. In the general case
+// make a direct system call since we want to keep in control of the broker
+// process' system calls profile to be able to loosely sandbox it.
+int sys_open(const char* pathname, int flags) {
+ // Always pass a defined |mode| in case flags mistakenly contains O_CREAT.
+ const int mode = 0;
+ if (IsRunningOnValgrind()) {
+ // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
+ return open(pathname, flags, mode);
+ } else {
+ return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
+ }
+}
+
static const size_t kMaxMessageLength = 4096;
// Some flags are local to the current process and cannot be sent over a Unix
@@ -132,11 +154,19 @@ BrokerProcess::BrokerProcess(int denied_errno,
BrokerProcess::~BrokerProcess() {
if (initialized_ && ipc_socketpair_ != -1) {
- close(ipc_socketpair_);
+ // Closing the socket should be enough to notify the child to die,
+ // unless it has been duplicated.
+ PCHECK(0 == IGNORE_EINTR(close(ipc_socketpair_)));
+ PCHECK(0 == kill(broker_pid_, SIGKILL));
+ siginfo_t process_info;
+ // Reap the child.
+ int ret = HANDLE_EINTR(waitid(P_PID, broker_pid_, &process_info, WEXITED));
+ PCHECK(0 == ret);
}
}
-bool BrokerProcess::Init(bool (*sandbox_callback)(void)) {
+bool BrokerProcess::Init(
+ const base::Callback<bool(void)>& broker_process_init_callback) {
CHECK(!initialized_);
int socket_pair[2];
// Use SOCK_SEQPACKET, because we need to preserve message boundaries
@@ -147,7 +177,9 @@ bool BrokerProcess::Init(bool (*sandbox_callback)(void)) {
return false;
}
+#if !defined(THREAD_SANITIZER)
DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+#endif
int child_pid = fork();
if (child_pid == -1) {
close(socket_pair[0]);
@@ -173,10 +205,7 @@ bool BrokerProcess::Init(bool (*sandbox_callback)(void)) {
shutdown(socket_pair[0], SHUT_WR);
ipc_socketpair_ = socket_pair[0];
is_child_ = true;
- // Enable the sandbox if provided.
- if (sandbox_callback) {
- CHECK(sandbox_callback());
- }
+ CHECK(broker_process_init_callback.Run());
initialized_ = true;
for (;;) {
HandleRequest();
@@ -292,8 +321,7 @@ int BrokerProcess::PathAndFlagsSyscall(enum IPCCommands syscall_type,
// that we will then close.
// A request should start with an int that will be used as the command type.
bool BrokerProcess::HandleRequest() const {
-
- std::vector<int> fds;
+ ScopedVector<base::ScopedFD> fds;
char buf[kMaxMessageLength];
errno = 0;
const ssize_t msg_len = UnixDomainSocket::RecvMsg(ipc_socketpair_, buf,
@@ -306,12 +334,13 @@ bool BrokerProcess::HandleRequest() const {
// The parent should send exactly one file descriptor, on which we
// will write the reply.
- if (msg_len < 0 || fds.size() != 1 || fds.at(0) < 0) {
+ // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'.
+ if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) {
PLOG(ERROR) << "Error reading message from the client";
return false;
}
- const int temporary_ipc = fds.at(0);
+ base::ScopedFD temporary_ipc(fds[0]->Pass());
Pickle pickle(buf, msg_len);
PickleIterator iter(pickle);
@@ -324,15 +353,13 @@ bool BrokerProcess::HandleRequest() const {
case kCommandOpen:
// We reply on the file descriptor sent to us via the IPC channel.
r = HandleRemoteCommand(static_cast<IPCCommands>(command_type),
- temporary_ipc, pickle, iter);
+ temporary_ipc.get(), pickle, iter);
break;
default:
NOTREACHED();
r = false;
break;
}
- int ret = IGNORE_EINTR(close(temporary_ipc));
- DCHECK(!ret) << "Could not close temporary IPC channel";
return r;
}
@@ -375,7 +402,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) {
+ it != opened_files.end(); ++it) {
int ret = IGNORE_EINTR(close(*it));
DCHECK(!ret) << "Could not close file descriptor";
}
@@ -423,9 +450,7 @@ void BrokerProcess::OpenFileForIPC(const std::string& requested_filename,
if (safe_to_open_file) {
CHECK(file_to_open);
- // We're doing a 2-parameter open, so we don't support O_CREAT. It doesn't
- // hurt to always pass a third argument though.
- int opened_fd = syscall(__NR_open, file_to_open, flags, 0);
+ int opened_fd = sys_open(file_to_open, flags);
if (opened_fd < 0) {
write_pickle->WriteInt(-errno);
} else {
diff --git a/chromium/sandbox/linux/services/broker_process.h b/chromium/sandbox/linux/services/broker_process.h
index 6b13b33046d..ddde42ebfc1 100644
--- a/chromium/sandbox/linux/services/broker_process.h
+++ b/chromium/sandbox/linux/services/broker_process.h
@@ -9,8 +9,10 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback_forward.h"
#include "base/pickle.h"
#include "base/process/process.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -24,7 +26,7 @@ namespace sandbox {
// 2. CHECK(open_broker.Init(NULL));
// 3. Enable sandbox.
// 4. Use open_broker.Open() to open files.
-class BrokerProcess {
+class SANDBOX_EXPORT BrokerProcess {
public:
// |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
@@ -42,9 +44,9 @@ class BrokerProcess {
~BrokerProcess();
// Will initialize the broker process. There should be no threads at this
// point, since we need to fork().
- // sandbox_callback is a function that should be called to enable the
- // sandbox in the broker.
- bool Init(bool (*sandbox_callback)(void));
+ // broker_process_init_callback will be called in the new broker process,
+ // after fork() returns.
+ bool Init(const base::Callback<bool(void)>& broker_process_init_callback);
// Can be used in place of access(). Will be async signal safe.
// X_OK will always return an error in practice since the broker process
@@ -95,6 +97,8 @@ class BrokerProcess {
const std::vector<std::string> allowed_w_files_; // Files allowed for write.
int ipc_socketpair_; // Our communication channel to parent or child.
DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerProcess);
+
+ friend class BrokerProcessTestHelper;
};
} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/broker_process_unittest.cc b/chromium/sandbox/linux/services/broker_process_unittest.cc
index f163ef99718..a1f38df3ae9 100644
--- a/chromium/sandbox/linux/services/broker_process_unittest.cc
+++ b/chromium/sandbox/linux/services/broker_process_unittest.cc
@@ -6,26 +6,37 @@
#include <errno.h>
#include <fcntl.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <algorithm>
#include <string>
#include <vector>
#include "base/basictypes.h"
+#include "base/bind.h"
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/tests/test_utils.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
-using file_util::ScopedFD;
-
namespace sandbox {
+class BrokerProcessTestHelper {
+ public:
+ static int get_ipc_socketpair(const BrokerProcess* broker) {
+ return broker->ipc_socketpair_;
+ }
+};
+
namespace {
// Creates and open a temporary file on creation and closes
@@ -60,13 +71,9 @@ class ScopedTemporaryFile {
DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
};
-} // namespace
+bool NoOpCallback() { return true; }
-#if defined(OS_ANDROID)
- #define DISABLE_ON_ANDROID(function) DISABLED_##function
-#else
- #define DISABLE_ON_ANDROID(function) function
-#endif
+} // namespace
TEST(BrokerProcess, CreateAndDestroy) {
std::vector<std::string> read_whitelist;
@@ -74,21 +81,18 @@ TEST(BrokerProcess, CreateAndDestroy) {
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();
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
// 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));
- ASSERT_EQ(WEXITSTATUS(status), 0);
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
}
TEST(BrokerProcess, TestOpenAccessNull) {
const std::vector<std::string> empty;
BrokerProcess open_broker(EPERM, empty, empty);
- ASSERT_TRUE(open_broker.Init(NULL));
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
int fd = open_broker.Open(NULL, O_RDONLY);
ASSERT_EQ(fd, -EFAULT);
@@ -119,7 +123,7 @@ void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
read_whitelist,
write_whitelist,
fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(NULL));
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
int fd = -1;
fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
@@ -270,12 +274,11 @@ void TestOpenCpuinfo(bool 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();
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
int fd = -1;
fd = open_broker->Open(kFileCpuInfo, O_RDWR);
- ScopedFD fd_closer(&fd);
+ base::ScopedFD fd_closer(fd);
ASSERT_EQ(fd, -EPERM);
// Check we can read /proc/cpuinfo.
@@ -287,7 +290,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);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
ASSERT_GE(cpuinfo_fd, 0);
char buf[3];
memset(buf, 0, sizeof(buf));
@@ -296,7 +299,7 @@ void TestOpenCpuinfo(bool fast_check_in_client) {
// Open cpuinfo directly.
int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
- ScopedFD cpuinfo_fd2_closer(&cpuinfo_fd2);
+ base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
ASSERT_GE(cpuinfo_fd2, 0);
char buf2[3];
memset(buf2, 1, sizeof(buf2));
@@ -309,13 +312,9 @@ void TestOpenCpuinfo(bool fast_check_in_client) {
// ourselves.
ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
open_broker.reset();
-
- // Now we check that the broker has exited properly.
- int status = 0;
- ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
- ASSERT_TRUE(WIFEXITED(status));
- ASSERT_EQ(WEXITSTATUS(status), 0);
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
}
// Run the same thing twice. The second time, we make sure that no security
@@ -340,7 +339,7 @@ TEST(BrokerProcess, OpenFileRW) {
whitelist.push_back(tempfile_name);
BrokerProcess open_broker(EPERM, whitelist, whitelist);
- ASSERT_TRUE(open_broker.Init(NULL));
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
// Check we can access that file with read or write.
int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
@@ -377,15 +376,19 @@ SANDBOX_TEST(BrokerProcess, BrokerDied) {
std::vector<std::string>(),
true /* fast_check_in_client */,
true /* quiet_failures_for_tests */);
- SANDBOX_ASSERT(open_broker.Init(NULL));
- pid_t broker_pid = open_broker.broker_pid();
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+ const pid_t broker_pid = open_broker.broker_pid();
SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
- // Now we check that the broker has exited properly.
- int status = 0;
- SANDBOX_ASSERT(waitpid(broker_pid, &status, 0) == broker_pid);
- SANDBOX_ASSERT(WIFSIGNALED(status));
- SANDBOX_ASSERT(WTERMSIG(status) == SIGKILL);
+ // Now we check that the broker has been signaled, but do not reap it.
+ siginfo_t process_info;
+ SANDBOX_ASSERT(HANDLE_EINTR(waitid(
+ P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
+ 0);
+ SANDBOX_ASSERT(broker_pid == process_info.si_pid);
+ SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
+ SANDBOX_ASSERT(SIGKILL == process_info.si_status);
+
// 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);
@@ -400,7 +403,7 @@ void TestOpenComplexFlags(bool fast_check_in_client) {
whitelist,
whitelist,
fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(NULL));
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
// Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
int fd = -1;
int ret = 0;
@@ -441,4 +444,60 @@ TEST(BrokerProcess, OpenComplexFlagsNoClientCheck) {
// expected.
}
+// We need to allow noise because the broker will log when it receives our
+// bogus IPCs.
+SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, RecvMsgDescriptorLeak) {
+ // Find the four lowest available file descriptors.
+ int available_fds[4];
+ SANDBOX_ASSERT(0 == pipe(available_fds));
+ SANDBOX_ASSERT(0 == pipe(available_fds + 2));
+
+ // Save one FD to send to the broker later, and close the others.
+ base::ScopedFD message_fd(available_fds[0]);
+ for (size_t i = 1; i < arraysize(available_fds); i++) {
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(available_fds[i])));
+ }
+
+ // Lower our file descriptor limit to just allow three more file descriptors
+ // to be allocated. (N.B., RLIMIT_NOFILE doesn't limit the number of file
+ // descriptors a process can have: it only limits the highest value that can
+ // be assigned to newly-created descriptors allocated by the process.)
+ const rlim_t fd_limit =
+ 1 + *std::max_element(available_fds,
+ available_fds + arraysize(available_fds));
+
+ // Valgrind doesn't allow changing the hard descriptor limit, so we only
+ // change the soft descriptor limit here.
+ struct rlimit rlim;
+ SANDBOX_ASSERT(0 == getrlimit(RLIMIT_NOFILE, &rlim));
+ SANDBOX_ASSERT(fd_limit <= rlim.rlim_cur);
+ rlim.rlim_cur = fd_limit;
+ SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
+
+ static const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back(kCpuInfo);
+
+ BrokerProcess open_broker(EPERM, read_whitelist, std::vector<std::string>());
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ const int ipc_fd = BrokerProcessTestHelper::get_ipc_socketpair(&open_broker);
+ SANDBOX_ASSERT(ipc_fd >= 0);
+
+ static const char kBogus[] = "not a pickle";
+ std::vector<int> fds;
+ fds.push_back(message_fd.get());
+
+ // The broker process should only have a couple spare file descriptors
+ // available, but for good measure we send it fd_limit bogus IPCs anyway.
+ for (rlim_t i = 0; i < fd_limit; ++i) {
+ SANDBOX_ASSERT(
+ UnixDomainSocket::SendMsg(ipc_fd, kBogus, sizeof(kBogus), fds));
+ }
+
+ const int fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ SANDBOX_ASSERT(fd >= 0);
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/credentials.cc b/chromium/sandbox/linux/services/credentials.cc
index 4f041dc2887..96702b1700a 100644
--- a/chromium/sandbox/linux/services/credentials.cc
+++ b/chromium/sandbox/linux/services/credentials.cc
@@ -7,21 +7,28 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
#include <stdio.h>
#include <sys/capability.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
#include "base/template_util.h"
+#include "base/third_party/valgrind/valgrind.h"
#include "base/threading/thread.h"
namespace {
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
struct CapFreeDeleter {
inline void operator()(cap_t cap) const {
int ret = cap_free(cap);
@@ -49,7 +56,7 @@ struct FILECloser {
}
};
-// Don't use ScopedFILE in base/file_util.h since it doesn't check fclose().
+// Don't use ScopedFILE in base since it doesn't check fclose().
// TODO(jln): fix base/.
typedef scoped_ptr<FILE, FILECloser> ScopedFILE;
@@ -146,6 +153,16 @@ bool ChrootToSafeEmptyDir() {
return is_chrooted;
}
+// CHECK() that an attempt to move to a new user namespace raised an expected
+// errno.
+void CheckCloneNewUserErrno(int error) {
+ // 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(error == EPERM || error == EUSERS || error == EINVAL ||
+ error == ENOSYS);
+}
+
} // namespace.
namespace sandbox {
@@ -156,6 +173,35 @@ Credentials::Credentials() {
Credentials::~Credentials() {
}
+int Credentials::CountOpenFds(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ int proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
+ PCHECK(0 <= proc_self_fd);
+
+ // 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);
+
+ int count = 0;
+ 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;
+ }
+
+ ++count;
+ }
+ return count;
+}
+
bool Credentials::HasOpenDirectory(int proc_fd) {
int proc_self_fd = -1;
if (proc_fd >= 0) {
@@ -175,7 +221,7 @@ bool Credentials::HasOpenDirectory(int proc_fd) {
return false;
}
}
- CHECK_GE(proc_self_fd, 0);
+ PCHECK(0 <= proc_self_fd);
// Ownership of proc_self_fd is transferred here, it must not be closed
// or modified afterwards except via dir.
@@ -231,6 +277,37 @@ scoped_ptr<std::string> Credentials::GetCurrentCapString() const {
return scoped_ptr<std::string> (new std::string(cap_text.get()));
}
+// static
+bool Credentials::SupportsNewUserNS() {
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+ // so always consider UserNS unsupported there.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+ // This is roughly a fork().
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWUSER | SIGCHLD, 0, 0, 0);
+
+ if (pid == -1) {
+ CheckCloneNewUserErrno(errno);
+ return false;
+ }
+
+ // The parent process could have had threads. In the child, these threads
+ // have disappeared. Make sure to not do anything in the child, as this is a
+ // fragile execution environment.
+ if (pid == 0) {
+ _exit(0);
+ }
+
+ // Always reap the child.
+ siginfo_t infop;
+ PCHECK(0 == HANDLE_EINTR(waitid(P_PID, pid, &infop, WEXITED)));
+
+ // clone(2) succeeded, we can use CLONE_NEWUSER.
+ return true;
+}
+
bool Credentials::MoveToNewUserNS() {
uid_t uid;
gid_t gid;
@@ -241,16 +318,14 @@ bool Credentials::MoveToNewUserNS() {
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) {
+ const int unshare_errno = errno;
VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
<< "on this kernel.";
+ CheckCloneNewUserErrno(unshare_errno);
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));
diff --git a/chromium/sandbox/linux/services/credentials.h b/chromium/sandbox/linux/services/credentials.h
index c23db930df2..99f8f322dff 100644
--- a/chromium/sandbox/linux/services/credentials.h
+++ b/chromium/sandbox/linux/services/credentials.h
@@ -15,17 +15,23 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "sandbox/sandbox_export.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 {
+class SANDBOX_EXPORT Credentials {
public:
Credentials();
~Credentials();
+ // Returns the number of file descriptors in the current process's FD
+ // table, excluding |proc_fd|, which should be a file descriptor for
+ // /proc.
+ int CountOpenFds(int proc_fd);
+
// 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
@@ -51,6 +57,13 @@ class Credentials {
// debugging and tests.
scoped_ptr<std::string> GetCurrentCapString() const;
+ // Returns whether the kernel supports CLONE_NEWUSER and whether it would be
+ // possible to immediately move to a new user namespace. There is no point
+ // in using this method right before calling MoveToNewUserNS(), simply call
+ // MoveToNewUserNS() immediately. This method is only useful to test kernel
+ // support ahead of time.
+ static bool SupportsNewUserNS();
+
// 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
diff --git a/chromium/sandbox/linux/services/credentials_unittest.cc b/chromium/sandbox/linux/services/credentials_unittest.cc
index 9160bf7a1ca..12ef32006ab 100644
--- a/chromium/sandbox/linux/services/credentials_unittest.cc
+++ b/chromium/sandbox/linux/services/credentials_unittest.cc
@@ -12,13 +12,12 @@
#include <unistd.h>
#include "base/file_util.h"
+#include "base/files/scoped_file.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 {
@@ -58,6 +57,18 @@ TEST(Credentials, CreateAndDestroy) {
scoped_ptr<Credentials> cred2(new Credentials);
}
+TEST(Credentials, CountOpenFds) {
+ base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
+ ASSERT_TRUE(proc_fd.is_valid());
+ Credentials creds;
+ int fd_count = creds.CountOpenFds(proc_fd.get());
+ int fd = open("/dev/null", O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(fd_count + 1, creds.CountOpenFds(proc_fd.get()));
+ ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
+ EXPECT_EQ(fd_count, creds.CountOpenFds(proc_fd.get()));
+}
+
TEST(Credentials, HasOpenDirectory) {
Credentials creds;
// No open directory should exist at startup.
@@ -65,7 +76,7 @@ TEST(Credentials, HasOpenDirectory) {
{
// Have a "/dev" file descriptor around.
int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
- ScopedFD dev_fd_closer(&dev_fd);
+ base::ScopedFD dev_fd_closer(dev_fd);
EXPECT_TRUE(creds.HasOpenDirectory(-1));
}
EXPECT_FALSE(creds.HasOpenDirectory(-1));
@@ -75,7 +86,7 @@ TEST(Credentials, HasOpenDirectoryWithFD) {
Credentials creds;
int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
- ScopedFD proc_fd_closer(&proc_fd);
+ base::ScopedFD proc_fd_closer(proc_fd);
ASSERT_LE(0, proc_fd);
// Don't pass |proc_fd|, an open directory (proc_fd) should
@@ -87,7 +98,7 @@ TEST(Credentials, HasOpenDirectoryWithFD) {
{
// Have a "/dev" file descriptor around.
int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
- ScopedFD dev_fd_closer(&dev_fd);
+ base::ScopedFD dev_fd_closer(dev_fd);
EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
}
@@ -112,11 +123,12 @@ SANDBOX_TEST(Credentials, GetCurrentCapString) {
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.");
+ bool moved_to_new_ns = creds.MoveToNewUserNS();
+ fprintf(stdout,
+ "Unprivileged CLONE_NEWUSER supported: %s\n",
+ moved_to_new_ns ? "true." : "false.");
fflush(stdout);
- if (!userns_supported) {
+ if (!moved_to_new_ns) {
fprintf(stdout, "This kernel does not support unprivileged namespaces. "
"USERNS tests will succeed without running.\n");
fflush(stdout);
@@ -127,6 +139,14 @@ SANDBOX_TEST(Credentials, MoveToNewUserNS) {
CHECK(!creds.HasAnyCapability());
}
+SANDBOX_TEST(Credentials, SupportsUserNS) {
+ Credentials creds;
+ creds.DropAllCapabilities();
+ bool user_ns_supported = Credentials::SupportsNewUserNS();
+ bool moved_to_new_ns = creds.MoveToNewUserNS();
+ CHECK_EQ(user_ns_supported, moved_to_new_ns);
+}
+
SANDBOX_TEST(Credentials, UidIsPreserved) {
Credentials creds;
creds.DropAllCapabilities();
@@ -182,7 +202,7 @@ TEST(Credentials, CanDetectRoot) {
ASSERT_TRUE(WorkingDirectoryIsRoot());
}
-SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) {
+SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(DropFileSystemAccessIsSafe)) {
Credentials creds;
CHECK(creds.DropAllCapabilities());
// Probably missing kernel support.
@@ -197,7 +217,7 @@ SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) {
// Check that after dropping filesystem access and dropping privileges
// it is not possible to regain capabilities.
-SANDBOX_TEST(Credentials, CannotRegainPrivileges) {
+SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(CannotRegainPrivileges)) {
Credentials creds;
CHECK(creds.DropAllCapabilities());
// Probably missing kernel support.
@@ -207,6 +227,7 @@ SANDBOX_TEST(Credentials, CannotRegainPrivileges) {
// The kernel should now prevent us from regaining capabilities because we
// are in a chroot.
+ CHECK(!Credentials::SupportsNewUserNS());
CHECK(!creds.MoveToNewUserNS());
}
diff --git a/chromium/sandbox/linux/services/init_process_reaper.h b/chromium/sandbox/linux/services/init_process_reaper.h
index 531d18cba11..840f6fcda7b 100644
--- a/chromium/sandbox/linux/services/init_process_reaper.h
+++ b/chromium/sandbox/linux/services/init_process_reaper.h
@@ -6,6 +6,7 @@
#define SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
#include "base/callback_forward.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -16,7 +17,8 @@ namespace sandbox {
// 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);
+SANDBOX_EXPORT bool CreateInitProcessReaper(
+ base::Closure* post_fork_parent_callback);
} // namespace sandbox.
diff --git a/chromium/sandbox/linux/services/libc_urandom_override.cc b/chromium/sandbox/linux/services/libc_urandom_override.cc
index c5c49bac8ca..33bb25d6b17 100644
--- a/chromium/sandbox/linux/services/libc_urandom_override.cc
+++ b/chromium/sandbox/linux/services/libc_urandom_override.cc
@@ -25,9 +25,9 @@ namespace sandbox {
static bool g_override_urandom = false;
// TODO(sergeyu): Currently InitLibcUrandomOverrides() doesn't work properly
-// under ASAN - it crashes content_unittests. Make sure it works properly and
-// enable it here. http://crbug.com/123263
-#if !defined(ADDRESS_SANITIZER)
+// under ASan or MSan - it crashes content_unittests. Make sure it works
+// properly and enable it here. http://crbug.com/123263
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
static void InitLibcFileIOFunctions();
static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
#endif
@@ -37,13 +37,13 @@ void InitLibcUrandomOverrides() {
base::GetUrandomFD();
g_override_urandom = true;
-#if !defined(ADDRESS_SANITIZER)
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
InitLibcFileIOFunctions));
#endif
}
-#if !defined(ADDRESS_SANITIZER)
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
static const char kUrandomDevPath[] = "/dev/urandom";
@@ -231,6 +231,6 @@ int stat64_override(const char *path, struct stat64 *buf) {
#endif // HAVE_XSTAT
-#endif // !defined(ADDRESS_SANITIZER)
+#endif // !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
} // namespace content
diff --git a/chromium/sandbox/linux/services/scoped_process.cc b/chromium/sandbox/linux/services/scoped_process.cc
new file mode 100644
index 00000000000..fd42a2a6e17
--- /dev/null
+++ b/chromium/sandbox/linux/services/scoped_process.cc
@@ -0,0 +1,119 @@
+// 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/scoped_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/thread_helpers.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSynchronisationChar[] = "D";
+
+void WaitForever() {
+ while(true) {
+ pause();
+ }
+}
+
+} // namespace
+
+ScopedProcess::ScopedProcess(const base::Closure& child_callback)
+ : child_process_id_(-1), process_id_(getpid()) {
+ PCHECK(0 == pipe(pipe_fds_));
+#if !defined(THREAD_SANITIZER)
+ // Make sure that we can safely fork().
+ CHECK(ThreadHelpers::IsSingleThreaded(-1));
+#endif
+ child_process_id_ = fork();
+ PCHECK(0 <= child_process_id_);
+
+ if (0 == child_process_id_) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ pipe_fds_[0] = -1;
+ child_callback.Run();
+ // Notify the parent that the closure has run.
+ CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1)));
+ WaitForever();
+ NOTREACHED();
+ _exit(1);
+ }
+
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ pipe_fds_[1] = -1;
+}
+
+ScopedProcess::~ScopedProcess() {
+ CHECK(IsOriginalProcess());
+ if (child_process_id_ >= 0) {
+ PCHECK(0 == kill(child_process_id_, SIGKILL));
+ siginfo_t process_info;
+
+ PCHECK(0 == HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED)));
+ }
+ if (pipe_fds_[0] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ }
+ if (pipe_fds_[1] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ }
+}
+
+int ScopedProcess::WaitForExit(bool* got_signaled) {
+ DCHECK(got_signaled);
+ CHECK(IsOriginalProcess());
+ siginfo_t process_info;
+ // WNOWAIT to make sure that the destructor can wait on the child.
+ int ret = HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT));
+ PCHECK(0 == ret) << "Did something else wait on the child?";
+
+ if (process_info.si_code == CLD_EXITED) {
+ *got_signaled = false;
+ } else if (process_info.si_code == CLD_KILLED ||
+ process_info.si_code == CLD_DUMPED) {
+ *got_signaled = true;
+ } else {
+ CHECK(false) << "ScopedProcess needs to be extended for si_code "
+ << process_info.si_code;
+ }
+ return process_info.si_status;
+}
+
+bool ScopedProcess::WaitForClosureToRun() {
+ char c = 0;
+ int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1));
+ PCHECK(ret >= 0);
+ if (0 == ret)
+ return false;
+
+ CHECK_EQ(c, kSynchronisationChar[0]);
+ return true;
+}
+
+// It would be problematic if after a fork(), another process would start using
+// this object.
+// This method allows to assert it is not happening.
+bool ScopedProcess::IsOriginalProcess() {
+ // Make a direct syscall to bypass glibc caching of PIDs.
+ int pid = syscall(__NR_getpid);
+ return pid == process_id_;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/scoped_process.h b/chromium/sandbox/linux/services/scoped_process.h
new file mode 100644
index 00000000000..a28131ee67d
--- /dev/null
+++ b/chromium/sandbox/linux/services/scoped_process.h
@@ -0,0 +1,55 @@
+// 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_SCOPED_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/process/process_handle.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// fork() a child process that will run a Closure.
+// After the Closure has run, the child will pause forever. If this object
+// is detroyed, the child will be destroyed, even if the closure did not
+// finish running. It's ok to signal the child from outside of this class to
+// destroy it.
+// This class cannot be instanciated from a multi-threaded process, as it needs
+// to fork().
+class SANDBOX_EXPORT ScopedProcess {
+ public:
+ // A new process will be created and |child_callback| will run in the child
+ // process. This callback is allowed to terminate the process or to simply
+ // return. If the callback returns, the process will wait forever.
+ explicit ScopedProcess(const base::Closure& child_callback);
+ ~ScopedProcess();
+
+ // Wait for the process to exit.
+ // |got_signaled| tells how to interpret the return value: either as an exit
+ // code, or as a signal number.
+ // When this returns, the process will still not have been reaped and will
+ // survive as a zombie for the lifetime of this object. This method can be
+ // called multiple times.
+ int WaitForExit(bool* got_signaled);
+
+ // Wait for the |child_callback| passed at construction to run. Return false
+ // if |child_callback| did not finish running and we know it never will (for
+ // instance the child crashed or used _exit()).
+ bool WaitForClosureToRun();
+ base::ProcessId GetPid() { return child_process_id_; }
+
+ private:
+ bool IsOriginalProcess();
+
+ base::ProcessId child_process_id_;
+ base::ProcessId process_id_;
+ int pipe_fds_[2];
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcess);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
diff --git a/chromium/sandbox/linux/services/scoped_process_unittest.cc b/chromium/sandbox/linux/services/scoped_process_unittest.cc
new file mode 100644
index 00000000000..382c7fb2cb5
--- /dev/null
+++ b/chromium/sandbox/linux/services/scoped_process_unittest.cc
@@ -0,0 +1,130 @@
+// 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 <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoExit() { _exit(0); }
+
+void ExitWithCode(int exit_code) { _exit(exit_code); }
+
+void RaiseAndExit(int signal) {
+ PCHECK(0 == raise(signal));
+ _exit(0);
+}
+
+void DoNothing() {}
+
+TEST(ScopedProcess, ScopedProcessNormalExit) {
+ const int kCustomExitCode = 12;
+ ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
+ bool got_signaled = true;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_FALSE(got_signaled);
+ EXPECT_EQ(kCustomExitCode, exit_code);
+
+ // Verify that WaitForExit() can be called multiple times on the same
+ // process.
+ bool got_signaled2 = true;
+ int exit_code2 = process.WaitForExit(&got_signaled2);
+ EXPECT_FALSE(got_signaled2);
+ EXPECT_EQ(kCustomExitCode, exit_code2);
+}
+
+// Disable this test on Android, SIGABRT is funky there.
+TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) {
+ PCHECK(SIG_ERR != signal(SIGABRT, SIG_DFL));
+ ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT));
+ bool got_signaled = false;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGABRT, exit_code);
+}
+
+TEST(ScopedProcess, ScopedProcessSignaled) {
+ ScopedProcess process(base::Bind(&DoNothing));
+ bool got_signaled = false;
+ ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGKILL, exit_code);
+}
+
+TEST(ScopedProcess, DiesForReal) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ { ScopedProcess process(base::Bind(&DoExit)); }
+
+ // Close writing end of the pipe.
+ write_end_closer.reset();
+ pipe_fds[1] = -1;
+
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c;
+ // If the child process is dead for real, there will be no writing end
+ // for this pipe left and read will EOF instead of returning EWOULDBLOCK.
+ ASSERT_EQ(0, read(pipe_fds[0], &c, 1));
+}
+
+TEST(ScopedProcess, SynchronizationBasic) {
+ ScopedProcess process1(base::Bind(&DoNothing));
+ EXPECT_TRUE(process1.WaitForClosureToRun());
+
+ ScopedProcess process2(base::Bind(&DoExit));
+ // The closure didn't finish running normally. This case is simple enough
+ // that process.WaitForClosureToRun() should return false, even though the
+ // API does not guarantees that it will return at all.
+ EXPECT_FALSE(process2.WaitForClosureToRun());
+}
+
+void SleepInMsAndWriteOneByte(int time_to_sleep, int fd) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(time_to_sleep));
+ CHECK(1 == write(fd, "1", 1));
+}
+
+TEST(ScopedProcess, SynchronizationWorks) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ // Start a process with a closure that takes a little bit to run.
+ ScopedProcess process(
+ base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1]));
+ EXPECT_TRUE(process.WaitForClosureToRun());
+
+ // Verify that the closure did, indeed, run.
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c = 0;
+ EXPECT_EQ(1, read(pipe_fds[0], &c, 1));
+ EXPECT_EQ('1', c);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/thread_helpers.cc b/chromium/sandbox/linux/services/thread_helpers.cc
index e0794f84a44..e820449cb70 100644
--- a/chromium/sandbox/linux/services/thread_helpers.cc
+++ b/chromium/sandbox/linux/services/thread_helpers.cc
@@ -5,6 +5,7 @@
#include "sandbox/linux/services/thread_helpers.h"
#include <errno.h>
+#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -21,7 +22,9 @@
namespace sandbox {
-bool ThreadHelpers::IsSingleThreaded(int proc_self_task) {
+namespace {
+
+bool IsSingleThreadedImpl(int proc_self_task) {
CHECK_LE(0, proc_self_task);
struct stat task_stat;
int fstat_ret = fstat(proc_self_task, &task_stat);
@@ -35,6 +38,21 @@ bool ThreadHelpers::IsSingleThreaded(int proc_self_task) {
return task_stat.st_nlink == 3;
}
+} // namespace
+
+bool ThreadHelpers::IsSingleThreaded(int proc_self_task) {
+ DCHECK_LE(-1, proc_self_task);
+ if (-1 == proc_self_task) {
+ const int task_fd = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
+ PCHECK(0 <= task_fd);
+ const bool result = IsSingleThreadedImpl(task_fd);
+ PCHECK(0 == IGNORE_EINTR(close(task_fd)));
+ return result;
+ } else {
+ return IsSingleThreadedImpl(proc_self_task);
+ }
+}
+
bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_self_task,
base::Thread* thread) {
DCHECK_LE(0, proc_self_task);
diff --git a/chromium/sandbox/linux/services/thread_helpers.h b/chromium/sandbox/linux/services/thread_helpers.h
index 651e5d9c501..377742a539a 100644
--- a/chromium/sandbox/linux/services/thread_helpers.h
+++ b/chromium/sandbox/linux/services/thread_helpers.h
@@ -6,16 +6,19 @@
#define SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
#include "base/basictypes.h"
+#include "sandbox/sandbox_export.h"
namespace base { class Thread; }
namespace sandbox {
-class ThreadHelpers {
+class SANDBOX_EXPORT 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.
+ // can be a file descriptor to /proc/self/task/ and remains owned by the
+ // caller or -1.
+ // If |proc_self_tasks| is -1, this method will open /proc/self/task/ and
+ // crash if it cannot.
static bool IsSingleThreaded(int proc_self_task);
// Stop |thread| and ensure that it does not have an entry in
@@ -28,6 +31,6 @@ class ThreadHelpers {
DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadHelpers);
};
-} // namespace content
+} // namespace sandbox
#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
index 725a62ef766..a36fd2975f8 100644
--- a/chromium/sandbox/linux/services/thread_helpers_unittests.cc
+++ b/chromium/sandbox/linux/services/thread_helpers_unittests.cc
@@ -17,6 +17,7 @@
#include "base/process/process_metrics.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
+#include "build/build_config.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -50,16 +51,31 @@ class ScopedProcSelfTask {
DISALLOW_COPY_AND_ASSIGN(ScopedProcSelfTask);
};
-TEST(ThreadHelpers, IsSingleThreadedBasic) {
+#if defined(THREAD_SANITIZER)
+// These tests fail under ThreadSanitizer, see http://crbug.com/342305
+#define MAYBE_IsSingleThreadedBasic DISABLED_IsSingleThreadedBasic
+#define MAYBE_IsSingleThreadedIterated DISABLED_IsSingleThreadedIterated
+#define MAYBE_IsSingleThreadedStartAndStop DISABLED_IsSingleThreadedStartAndStop
+#else
+#define MAYBE_IsSingleThreadedBasic IsSingleThreadedBasic
+#define MAYBE_IsSingleThreadedIterated IsSingleThreadedIterated
+#define MAYBE_IsSingleThreadedStartAndStop IsSingleThreadedStartAndStop
+#endif
+
+TEST(ThreadHelpers, MAYBE_IsSingleThreadedBasic) {
ScopedProcSelfTask task;
ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(-1));
base::Thread thread("sandbox_tests");
ASSERT_TRUE(thread.Start());
ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(-1));
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread));
}
-TEST(ThreadHelpers, IsSingleThreadedIterated) {
+TEST(ThreadHelpers, MAYBE_IsSingleThreadedIterated) {
ScopedProcSelfTask task;
ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
@@ -68,10 +84,12 @@ TEST(ThreadHelpers, IsSingleThreadedIterated) {
base::Thread thread("sandbox_tests");
ASSERT_TRUE(thread.Start());
ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread));
}
}
-TEST(ThreadHelpers, IsSingleThreadedStartAndStop) {
+TEST(ThreadHelpers, MAYBE_IsSingleThreadedStartAndStop) {
ScopedProcSelfTask task;
ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
diff --git a/chromium/sandbox/linux/services/unix_domain_socket_unittest.cc b/chromium/sandbox/linux/services/unix_domain_socket_unittest.cc
new file mode 100644
index 00000000000..17208a829dd
--- /dev/null
+++ b/chromium/sandbox/linux/services/unix_domain_socket_unittest.cc
@@ -0,0 +1,267 @@
+// 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 <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/process_handle.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+// Additional tests for base's UnixDomainSocket to make sure it behaves
+// correctly in the presence of sandboxing functionality (e.g., receiving
+// PIDs across namespaces).
+
+namespace sandbox {
+
+namespace {
+
+const char kHello[] = "hello";
+
+// If the calling process isn't root, then try using unshare(CLONE_NEWUSER)
+// to fake it.
+void FakeRoot() {
+ // If we're already root, then allow test to proceed.
+ if (geteuid() == 0)
+ return;
+
+ // Otherwise hope the kernel supports unprivileged namespaces.
+ if (unshare(CLONE_NEWUSER) == 0)
+ return;
+
+ printf("Permission to use CLONE_NEWPID missing; skipping test.\n");
+ UnitTests::IgnoreThisTest();
+}
+
+void WaitForExit(pid_t pid) {
+ int status;
+ CHECK_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(0, WEXITSTATUS(status));
+}
+
+base::ProcessId GetParentProcessId(base::ProcessId pid) {
+ // base::GetParentProcessId() is defined as taking a ProcessHandle instead of
+ // a ProcessId, even though it's a POSIX-only function and IDs and Handles
+ // are both simply pid_t on POSIX... :/
+ base::ProcessHandle handle;
+ CHECK(base::OpenProcessHandle(pid, &handle));
+ base::ProcessId ret = base::GetParentProcessId(pid);
+ base::CloseProcessHandle(handle);
+ return ret;
+}
+
+// SendHello sends a "hello" to socket fd, and then blocks until the recipient
+// acknowledges it by calling RecvHello.
+void SendHello(int fd) {
+ int pipe_fds[2];
+ CHECK_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_pipe(pipe_fds[0]);
+ base::ScopedFD write_pipe(pipe_fds[1]);
+
+ std::vector<int> send_fds;
+ send_fds.push_back(write_pipe.get());
+ CHECK(UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds));
+
+ write_pipe.reset();
+
+ // Block until receiver closes their end of the pipe.
+ char ch;
+ CHECK_EQ(0, HANDLE_EINTR(read(read_pipe.get(), &ch, 1)));
+}
+
+// RecvHello receives and acknowledges a "hello" on socket fd, and returns the
+// process ID of the sender in sender_pid. Optionally, write_pipe can be used
+// to return a file descriptor, and the acknowledgement will be delayed until
+// the descriptor is closed.
+// (Implementation details: SendHello allocates a new pipe, sends us the writing
+// end alongside the "hello" message, and then blocks until we close the writing
+// end of the pipe.)
+void RecvHello(int fd,
+ base::ProcessId* sender_pid,
+ base::ScopedFD* write_pipe = NULL) {
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ ScopedVector<base::ScopedFD> message_fds;
+ ssize_t n = UnixDomainSocket::RecvMsgWithPid(
+ fd, buf, sizeof(buf), &message_fds, sender_pid);
+ CHECK_EQ(sizeof(kHello), static_cast<size_t>(n));
+ CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ CHECK_EQ(1U, message_fds.size());
+ if (write_pipe)
+ write_pipe->swap(*message_fds[0]);
+}
+
+// Check that receiving PIDs works across a fork().
+SANDBOX_TEST(UnixDomainSocketTest, Fork) {
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = fork();
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Similar to Fork above, but forking the child into a new pid namespace.
+SANDBOX_TEST(UnixDomainSocketTest, Namespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ // Check that we think we're pid 1 in our new namespace.
+ CHECK_EQ(1, syscall(__NR_getpid));
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Again similar to Fork, but now with nested PID namespaces.
+SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid2);
+
+ if (pid2 != 0) {
+ // Wait for grandchild to run to completion; see comments below.
+ WaitForExit(pid2);
+
+ // Fallthrough once grandchild has sent its hello and exited.
+ }
+
+ // Check that we think we're pid 1.
+ CHECK_EQ(1, syscall(__NR_getpid));
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ // We have two messages to receive: first from the grand-child,
+ // then from the child.
+ for (unsigned iteration = 0; iteration < 2; ++iteration) {
+ base::ProcessId sender_pid;
+ base::ScopedFD pipe_fd;
+ RecvHello(recv_sock.get(), &sender_pid, &pipe_fd);
+
+ // We need our child and grandchild processes to both be alive for
+ // GetParentProcessId() to return a valid pid, hence the pipe trickery.
+ // (On the first iteration, grandchild is blocked reading from the pipe
+ // until we close it, and child is blocked waiting for grandchild to exit.)
+ switch (iteration) {
+ case 0: // Grandchild's message
+ // Check that sender_pid refers to our grandchild by checking that pid
+ // (our child) is its parent.
+ CHECK_EQ(pid, GetParentProcessId(sender_pid));
+ break;
+ case 1: // Child's message
+ CHECK_EQ(pid, sender_pid);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ WaitForExit(pid);
+}
+
+// Tests that GetPeerPid() returns 0 if the peer does not exist in caller's
+// namespace.
+SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD send_sock(fds[0]);
+ base::ScopedFD recv_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(0, sender_pid);
+ _exit(0);
+ }
+
+ // Parent process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ WaitForExit(pid);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/yama.cc b/chromium/sandbox/linux/services/yama.cc
new file mode 100644
index 00000000000..49e1b36aab3
--- /dev/null
+++ b/chromium/sandbox/linux/services/yama.cc
@@ -0,0 +1,116 @@
+// 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/yama.h"
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+#if !defined(PR_SET_PTRACER_ANY)
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// Enable or disable the Yama ptracers restrictions.
+// Return false if Yama is not present on this kernel.
+bool SetYamaPtracersRestriction(bool enable_restrictions) {
+ unsigned long set_ptracer_arg;
+ if (enable_restrictions) {
+ set_ptracer_arg = 0;
+ } else {
+ set_ptracer_arg = PR_SET_PTRACER_ANY;
+ }
+
+ const int ret = prctl(PR_SET_PTRACER, set_ptracer_arg);
+ const int prctl_errno = errno;
+
+ if (0 == ret) {
+ return true;
+ } else {
+ // ENOSYS or EINVAL means Yama is not in the current kernel.
+ CHECK(ENOSYS == prctl_errno || EINVAL == prctl_errno);
+ return false;
+ }
+}
+
+bool CanAccessProcFS() {
+ static const char kProcfsKernelSysPath[] = "/proc/sys/kernel/";
+ int ret = access(kProcfsKernelSysPath, F_OK);
+ if (ret) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+// static
+bool Yama::RestrictPtracersToAncestors() {
+ return SetYamaPtracersRestriction(true /* enable_restrictions */);
+}
+
+// static
+bool Yama::DisableYamaRestrictions() {
+ return SetYamaPtracersRestriction(false /* enable_restrictions */);
+}
+
+// static
+int Yama::GetStatus() {
+ if (!CanAccessProcFS()) {
+ return 0;
+ }
+
+ static const char kPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope";
+
+ base::ScopedFD yama_scope(HANDLE_EINTR(open(kPtraceScopePath, O_RDONLY)));
+
+ if (!yama_scope.is_valid()) {
+ const int open_errno = errno;
+ DCHECK(ENOENT == open_errno);
+ // The status is known, yama is not present.
+ return STATUS_KNOWN;
+ }
+
+ char yama_scope_value = 0;
+ ssize_t num_read = HANDLE_EINTR(read(yama_scope.get(), &yama_scope_value, 1));
+ PCHECK(1 == num_read);
+
+ switch (yama_scope_value) {
+ case '0':
+ return STATUS_KNOWN | STATUS_PRESENT;
+ case '1':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING;
+ case '2':
+ case '3':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING |
+ STATUS_STRICT_ENFORCING;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// static
+bool Yama::IsPresent() { return GetStatus() & STATUS_PRESENT; }
+
+// static
+bool Yama::IsEnforcing() { return GetStatus() & STATUS_ENFORCING; }
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/yama.h b/chromium/sandbox/linux/services/yama.h
new file mode 100644
index 00000000000..20c28bae148
--- /dev/null
+++ b/chromium/sandbox/linux/services/yama.h
@@ -0,0 +1,58 @@
+// 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_YAMA_H_
+#define SANDBOX_LINUX_SERVICES_YAMA_H_
+
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Yama is a LSM kernel module which can restrict ptrace().
+// This class provides ways to detect if Yama is present and enabled
+// and to restrict which processes can ptrace the current process.
+class SANDBOX_EXPORT Yama {
+ public:
+ // This enum should be used to set or check a bitmask.
+ // A value of 0 would indicate that the status is not known.
+ enum GlobalStatus {
+ STATUS_KNOWN = 1 << 0,
+ STATUS_PRESENT = 1 << 1,
+ STATUS_ENFORCING = 1 << 2,
+ // STATUS_STRICT_ENFORCING corresponds to either mode 2 or mode 3 of Yama.
+ // Ptrace could be entirely denied, or restricted to CAP_SYS_PTRACE
+ // and PTRACE_TRACEME.
+ STATUS_STRICT_ENFORCING = 1 << 3
+ };
+
+ // Restrict who can ptrace() the current process to its ancestors.
+ // If this succeeds, then Yama is available on this kernel.
+ // However, Yama may not be enforcing at this time.
+ static bool RestrictPtracersToAncestors();
+
+ // Disable Yama restrictions for the current process.
+ // This will fail if Yama is not available on this kernel.
+ // This is meant for testing only. If you need this, implement
+ // a per-pid authorization instead.
+ static bool DisableYamaRestrictions();
+
+ // Checks if Yama is currently in enforcing mode for the machine (not the
+ // current process). This requires access to the filesystem and will use
+ // /proc/sys/kernel/yama/ptrace_scope.
+ static int GetStatus();
+
+ // Helper for checking for STATUS_PRESENT in GetStatus().
+ static bool IsPresent();
+ // Helper for checkking for STATUS_ENFORCING in GetStatus().
+ static bool IsEnforcing();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Yama);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_YAMA_H_
diff --git a/chromium/sandbox/linux/services/yama_unittests.cc b/chromium/sandbox/linux/services/yama_unittests.cc
new file mode 100644
index 00000000000..17ef4b40de5
--- /dev/null
+++ b/chromium/sandbox/linux/services/yama_unittests.cc
@@ -0,0 +1,152 @@
+// 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 <errno.h>
+#include <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/services/yama.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool CanPtrace(pid_t pid) {
+ int ret;
+ ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
+ if (ret == -1) {
+ CHECK_EQ(EPERM, errno);
+ return false;
+ }
+ // Wait for the process to be stopped so that it can be detached.
+ siginfo_t process_info;
+ int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
+ PCHECK(0 == wait_ret);
+ PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
+ return true;
+}
+
+// _exit(0) if pid can be ptraced by the current process.
+// _exit(1) otherwise.
+void ExitZeroIfCanPtrace(pid_t pid) {
+ if (CanPtrace(pid)) {
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+}
+
+bool CanSubProcessPtrace(pid_t pid) {
+ ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
+ bool signaled;
+ int exit_code = process.WaitForExit(&signaled);
+ CHECK(!signaled);
+ return 0 == exit_code;
+}
+
+// The tests below assume that the system-level configuration will not change
+// while they run.
+
+TEST(Yama, GetStatus) {
+ int status1 = Yama::GetStatus();
+
+ // Check that the value is a possible bitmask.
+ ASSERT_LE(0, status1);
+ ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
+ Yama::STATUS_STRICT_ENFORCING,
+ status1);
+
+ // The status should not just be a random value.
+ int status2 = Yama::GetStatus();
+ EXPECT_EQ(status1, status2);
+
+ // This test is not running sandboxed, there is no reason to not know the
+ // status.
+ EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
+
+ if (status1 & Yama::STATUS_STRICT_ENFORCING) {
+ // If Yama is strictly enforcing, it is also enforcing.
+ EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
+ }
+
+ if (status1 & Yama::STATUS_ENFORCING) {
+ // If Yama is enforcing, Yama is present.
+ EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
+ }
+
+ // Verify that the helper functions work as intended.
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
+ Yama::IsEnforcing());
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
+ Yama::IsPresent());
+
+ fprintf(stdout,
+ "Yama present: %s - enforcing: %s\n",
+ Yama::IsPresent() ? "Y" : "N",
+ Yama::IsEnforcing() ? "Y" : "N");
+}
+
+SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
+ // This call will succeed iff Yama is present.
+ bool restricted = Yama::RestrictPtracersToAncestors();
+ CHECK_EQ(restricted, Yama::IsPresent());
+}
+
+// Attempts to enable or disable Yama restrictions.
+void SetYamaRestrictions(bool enable_restriction) {
+ if (enable_restriction) {
+ Yama::RestrictPtracersToAncestors();
+ } else {
+ Yama::DisableYamaRestrictions();
+ }
+}
+
+TEST(Yama, RestrictPtraceWorks) {
+ ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
+ ASSERT_TRUE(process1.WaitForClosureToRun());
+
+ if (Yama::IsEnforcing()) {
+ // A sibling process cannot ptrace process1.
+ ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
+ }
+
+ if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
+ // However, parent can ptrace process1.
+ ASSERT_TRUE(CanPtrace(process1.GetPid()));
+
+ // A sibling can ptrace process2 which disables any Yama protection.
+ ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
+ ASSERT_TRUE(process2.WaitForClosureToRun());
+ ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
+ }
+}
+
+void DoNothing() {}
+
+SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
+ if (!Yama::IsPresent())
+ return;
+
+ CHECK(Yama::DisableYamaRestrictions());
+ ScopedProcess process1(base::Bind(&DoNothing));
+
+ if (Yama::IsEnforcing()) {
+ // Check that process1 is protected by Yama, even though it has
+ // been created from a process that disabled Yama.
+ CHECK(!CanSubProcessPtrace(process1.GetPid()));
+ }
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/suid/client/DEPS b/chromium/sandbox/linux/suid/client/DEPS
new file mode 100644
index 00000000000..99a337d7729
--- /dev/null
+++ b/chromium/sandbox/linux/suid/client/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/services",
+]
diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc
index 740823a8b36..fc03cdd099e 100644
--- a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -2,23 +2,40 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#include "base/command_line.h"
#include "base/environment.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/process/process_metrics.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"
namespace {
+bool IsFileSystemAccessDenied() {
+ base::ScopedFD self_exe(HANDLE_EINTR(open(base::kProcSelfExe, O_RDONLY)));
+ return !self_exe.is_valid();
+}
+
// Set an environment variable that reflects the API version we expect from the
// setuid sandbox. Old versions of the sandbox will ignore this.
void SetSandboxAPIEnvironmentVariable(base::Environment* env) {
@@ -26,6 +43,26 @@ void SetSandboxAPIEnvironmentVariable(base::Environment* env) {
base::IntToString(sandbox::kSUIDSandboxApiNumber));
}
+// Unset environment variables that are expected to be set by the setuid
+// sandbox. This is to allow nesting of one instance of the SUID sandbox
+// inside another.
+void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) {
+ DCHECK(env_map);
+ const base::NativeEnvironmentString environment_vars[] = {
+ sandbox::kSandboxDescriptorEnvironmentVarName,
+ sandbox::kSandboxHelperPidEnvironmentVarName,
+ sandbox::kSandboxEnvironmentApiProvides,
+ sandbox::kSandboxPIDNSEnvironmentVarName,
+ sandbox::kSandboxNETNSEnvironmentVarName,
+ };
+
+ for (size_t i = 0; i < arraysize(environment_vars); ++i) {
+ // Setting values in EnvironmentMap to an empty-string will make
+ // sure that they get unset from the environment via AlterEnvironment().
+ (*env_map)[environment_vars[i]] = base::NativeEnvironmentString();
+ }
+}
+
// Wrapper around a shared C function.
// Returns the "saved" environment variable name corresponding to |envvar|
// in a new string or NULL.
@@ -91,13 +128,17 @@ int GetIPCDescriptor(base::Environment* env) {
return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName);
}
+const char* GetDevelSandboxPath() {
+ return getenv("CHROME_DEVEL_SANDBOX");
+}
+
} // namespace
namespace sandbox {
SetuidSandboxClient* SetuidSandboxClient::Create() {
base::Environment* environment(base::Environment::Create());
- SetuidSandboxClient* sandbox_client(new(SetuidSandboxClient));
+ SetuidSandboxClient* sandbox_client(new SetuidSandboxClient);
CHECK(environment);
sandbox_client->env_ = environment;
@@ -113,6 +154,21 @@ SetuidSandboxClient::~SetuidSandboxClient() {
delete env_;
}
+void SetuidSandboxClient::CloseDummyFile() {
+ // When we're launched through the setuid sandbox, SetupLaunchOptions
+ // arranges for kZygoteIdFd to be a dummy file descriptor to satisfy an
+ // ancient setuid sandbox ABI requirement. However, the descriptor is no
+ // longer needed, so we can simply close it right away now.
+ CHECK(IsSuidSandboxChild());
+
+ // Sanity check that kZygoteIdFd refers to a pipe.
+ struct stat st;
+ PCHECK(0 == fstat(kZygoteIdFd, &st));
+ CHECK(S_ISFIFO(st.st_mode));
+
+ PCHECK(0 == IGNORE_EINTR(close(kZygoteIdFd)));
+}
+
bool SetuidSandboxClient::ChrootMe() {
int ipc_fd = GetIPCDescriptor(env_);
@@ -129,7 +185,7 @@ bool SetuidSandboxClient::ChrootMe() {
// We need to reap the chroot helper process in any event.
pid_t helper_pid = GetHelperPID(env_);
// If helper_pid is -1 we wait for any child.
- if (waitpid(helper_pid, NULL, 0) < 0) {
+ if (HANDLE_EINTR(waitpid(helper_pid, NULL, 0)) < 0) {
PLOG(ERROR) << "Failed to wait for setuid helper to die";
return false;
}
@@ -147,6 +203,7 @@ bool SetuidSandboxClient::ChrootMe() {
// We now consider ourselves "fully sandboxed" as far as the
// setuid sandbox is concerned.
+ CHECK(IsFileSystemAccessDenied());
sandboxed_ = true;
return true;
}
@@ -176,10 +233,87 @@ bool SetuidSandboxClient::IsSandboxed() const {
return sandboxed_;
}
+// Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables
+// the setuid sandbox. TODO(jln): fix this (crbug.com/245376).
+bool SetuidSandboxClient::IsDisabledViaEnvironment() {
+ const char* devel_sandbox_path = GetDevelSandboxPath();
+ if (devel_sandbox_path && '\0' == *devel_sandbox_path) {
+ return true;
+ }
+ return false;
+}
+
+base::FilePath SetuidSandboxClient::GetSandboxBinaryPath() {
+ base::FilePath sandbox_binary;
+ base::FilePath exe_dir;
+ if (PathService::Get(base::DIR_EXE, &exe_dir)) {
+ base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
+ if (base::PathExists(sandbox_candidate))
+ sandbox_binary = sandbox_candidate;
+ }
+
+ // In user-managed builds, including development builds, an environment
+ // variable is required to enable the sandbox. See
+ // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
+ struct stat st;
+ if (sandbox_binary.empty() && stat(base::kProcSelfExe, &st) == 0 &&
+ st.st_uid == getuid()) {
+ const char* devel_sandbox_path = GetDevelSandboxPath();
+ if (devel_sandbox_path) {
+ sandbox_binary = base::FilePath(devel_sandbox_path);
+ }
+ }
+
+ return sandbox_binary;
+}
+
+void SetuidSandboxClient::PrependWrapper(base::CommandLine* cmd_line) {
+ std::string sandbox_binary(GetSandboxBinaryPath().value());
+ struct stat st;
+ if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) {
+ LOG(FATAL) << "The SUID sandbox helper binary is missing: "
+ << sandbox_binary << " Aborting now. See "
+ "https://code.google.com/p/chromium/wiki/"
+ "LinuxSUIDSandboxDevelopment.";
+ }
+
+ if (access(sandbox_binary.c_str(), X_OK) != 0 || (st.st_uid != 0) ||
+ ((st.st_mode & S_ISUID) == 0) || ((st.st_mode & S_IXOTH)) == 0) {
+ LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
+ "configured correctly. Rather than run without sandboxing "
+ "I'm aborting now. You need to make sure that "
+ << sandbox_binary << " is owned by root and has mode 4755.";
+ }
+
+ cmd_line->PrependWrapper(sandbox_binary);
+}
+
+void SetuidSandboxClient::SetupLaunchOptions(
+ base::LaunchOptions* options,
+ base::FileHandleMappingVector* fds_to_remap,
+ base::ScopedFD* dummy_fd) {
+ DCHECK(options);
+ DCHECK(fds_to_remap);
+
+ // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used.
+ options->allow_new_privs = true;
+ UnsetExpectedEnvironmentVariables(&options->environ);
+
+ // Set dummy_fd to the reading end of a closed pipe.
+ int pipe_fds[2];
+ PCHECK(0 == pipe(pipe_fds));
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1])));
+ dummy_fd->reset(pipe_fds[0]);
+
+ // We no longer need a dummy socket for discovering the child's PID,
+ // but the sandbox is still hard-coded to expect a file descriptor at
+ // kZygoteIdFd. Fixing this requires a sandbox API change. :(
+ fds_to_remap->push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd));
+}
+
void SetuidSandboxClient::SetupLaunchEnvironment() {
SaveSUIDUnsafeEnvironmentVariables(env_);
SetSandboxAPIEnvironmentVariable(env_);
}
} // namespace sandbox
-
diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h
index 5a6724dadd8..e6a3e4c5cad 100644
--- a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h
@@ -7,26 +7,47 @@
#include "base/basictypes.h"
#include "base/callback_forward.h"
-
-namespace base { class Environment; }
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/process/launch.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
// Helper class to use the setuid sandbox. This class is to be used both
// before launching the setuid helper and after being executed through the
// setuid helper.
+// This class is difficult to use. It has been created by refactoring very old
+// code scathered through the Chromium code base.
//
-// A typical use would be:
-// 1. The browser calls SetupLaunchEnvironment()
-// 2. The browser launches a renderer through the setuid sandbox.
-// 3. The renderer requests being chroot-ed through ChrootMe() and
+// A typical use for "A" launching a sandboxed process "B" would be:
+// 1. A calls SetupLaunchEnvironment()
+// 2. A sets up a CommandLine and then amends it with
+// PrependWrapper() (or manually, by relying on GetSandboxBinaryPath()).
+// 3. A uses SetupLaunchOptions() to arrange for a dummy descriptor for the
+// setuid sandbox ABI.
+// 4. A launches B with base::LaunchProcess, using the amended CommandLine.
+// 5. B uses CloseDummyFile() to close the dummy file descriptor.
+// 6. B performs various initializations that require access to the file
+// system.
+// 6.b (optional) B uses sandbox::Credentials::HasOpenDirectory() to verify
+// that no directory is kept open (which would allow bypassing the setuid
+// sandbox).
+// 7. B should be prepared to assume the role of init(1). In particular, B
+// cannot receive any signal from any other process, excluding SIGKILL.
+// If B dies, all the processes in the namespace will die.
+// B can fork() and the parent can assume the role of init(1), by using
+// CreateInitProcessReaper().
+// 8. B requests being chroot-ed through ChrootMe() and
// requests other sandboxing status via the status functions.
-class SetuidSandboxClient {
+class SANDBOX_EXPORT SetuidSandboxClient {
public:
// All instantation should go through this factory method.
static class SetuidSandboxClient* Create();
~SetuidSandboxClient();
+ // Close the dummy file descriptor leftover from the sandbox ABI.
+ void CloseDummyFile();
// Ask the setuid helper over the setuid sandbox IPC channel to chroot() us
// to an empty directory.
// Will only work if we have been launched through the setuid helper.
@@ -48,18 +69,38 @@ class SetuidSandboxClient {
// Are we done and fully sandboxed ?
bool IsSandboxed() const;
+ // The setuid sandbox may still be disabled via the environment.
+ // This is tracked in crbug.com/245376.
+ bool IsDisabledViaEnvironment();
+ // Get the sandbox binary path. This method knows about the
+ // CHROME_DEVEL_SANDBOX environment variable used for user-managed builds. If
+ // the sandbox binary cannot be found, it will return an empty FilePath.
+ base::FilePath GetSandboxBinaryPath();
+ // Modify |cmd_line| to launch via the setuid sandbox. Crash if the setuid
+ // sandbox binary cannot be found. |cmd_line| must not be NULL.
+ void PrependWrapper(base::CommandLine* cmd_line);
+ // Set-up the launch options for launching via the setuid sandbox. Caller is
+ // responsible for keeping |dummy_fd| alive until LaunchProcess() completes.
+ // |options| and |fds_to_remap| must not be NULL.
+ // (Keeping |dummy_fd| alive is an unfortunate historical artifact of the
+ // chrome-sandbox ABI.)
+ void SetupLaunchOptions(base::LaunchOptions* options,
+ base::FileHandleMappingVector* fds_to_remap,
+ base::ScopedFD* dummy_fd);
// Set-up the environment. This should be done prior to launching the setuid
// helper.
void SetupLaunchEnvironment();
private:
+ SetuidSandboxClient();
+
// Holds the environment. Will never be NULL.
base::Environment* env_;
bool sandboxed_;
- DISALLOW_IMPLICIT_CONSTRUCTORS(SetuidSandboxClient);
+
+ DISALLOW_COPY_AND_ASSIGN(SetuidSandboxClient);
};
} // namespace sandbox
#endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
-
diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
index 552dc4b9d9c..d4f7dfef325 100644
--- a/chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
@@ -6,10 +6,9 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
+#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
#include "sandbox/linux/suid/common/sandbox.h"
-#include "setuid_sandbox_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
namespace sandbox {
@@ -90,5 +89,13 @@ TEST(SetuidSandboxClient, SandboxedClientAPI) {
EXPECT_FALSE(sandbox_client->IsSandboxed());
}
+// This test doesn't accomplish much, but will make sure that analysis tools
+// will run this codepath.
+TEST(SetuidSandboxClient, GetSandboxBinaryPath) {
+ scoped_ptr<SetuidSandboxClient> setuid_sandbox_client(
+ SetuidSandboxClient::Create());
+ ignore_result(setuid_sandbox_client->GetSandboxBinaryPath());
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h b/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
index 1132a7a7161..33ba4b6ab72 100644
--- a/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
+++ b/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
@@ -54,7 +54,7 @@ static inline char* SandboxSavedEnvironmentVariable(const char* envvar) {
const size_t envvar_len = strlen(envvar);
const size_t kMaxSizeT = (size_t) -1;
- if (envvar_len > kMaxSizeT - 1 -8)
+ if (envvar_len > kMaxSizeT - 1 - 8)
return NULL;
const size_t saved_envvarlen = envvar_len + 1 /* NUL terminator */ +
diff --git a/chromium/sandbox/linux/suid/linux_util.c b/chromium/sandbox/linux/suid/linux_util.c
index 256468ff4ea..9febe6d9cf2 100644
--- a/chromium/sandbox/linux/suid/linux_util.c
+++ b/chromium/sandbox/linux/suid/linux_util.c
@@ -5,8 +5,12 @@
// The following is duplicated from base/linux_utils.cc.
// We shouldn't link against C++ code in a setuid binary.
-#define _GNU_SOURCE // For O_DIRECTORY
-#include "linux_util.h"
+// Needed for O_DIRECTORY, must be defined before fcntl.h is included
+// (and it can be included earlier than the explicit #include below
+// in some versions of glibc).
+#define _GNU_SOURCE
+
+#include "sandbox/linux/suid/linux_util.h"
#include <dirent.h>
#include <errno.h>
@@ -26,7 +30,8 @@ static const char kSocketLinkPrefix[] = "socket:[";
// socket.
// inode_out: (output) set to the inode number on success
// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
-static bool ProcPathGetInodeAt(ino_t* inode_out, int base_dir_fd,
+static bool ProcPathGetInodeAt(ino_t* inode_out,
+ int base_dir_fd,
const char* path) {
// We also check that the path is relative.
if (!inode_out || !path || *path == '/')
@@ -40,7 +45,7 @@ static bool ProcPathGetInodeAt(ino_t* inode_out, int base_dir_fd,
if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1))
return false;
- char *endptr = NULL;
+ char* endptr = NULL;
errno = 0;
const unsigned long long int inode_ull =
strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
@@ -72,7 +77,7 @@ bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
const uid_t uid = getuid();
struct dirent* dent;
while ((dent = readdir(proc))) {
- char *endptr = NULL;
+ char* endptr = NULL;
errno = 0;
const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
diff --git a/chromium/sandbox/linux/suid/process_util.h b/chromium/sandbox/linux/suid/process_util.h
index f6b4c314e63..9fb9a8791a7 100644
--- a/chromium/sandbox/linux/suid/process_util.h
+++ b/chromium/sandbox/linux/suid/process_util.h
@@ -11,8 +11,6 @@
#include <stdbool.h>
#include <sys/types.h>
-#include "base/base_export.h"
-
// This adjusts /proc/process/oom_score_adj so the Linux OOM killer
// will prefer certain process types over others. The range for the
// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
@@ -21,12 +19,12 @@
// try to set the older oom_adj value instead, scaling the score to
// the required range of [0, 15]. This may result in some aliasing of
// values, of course.
-BASE_EXPORT bool AdjustOOMScore(pid_t process, int score);
+bool AdjustOOMScore(pid_t process, int score);
// This adjusts /sys/kernel/mm/chromeos-low_mem/margin so that
// the kernel notifies us that we are low on memory when less than
// |margin_mb| megabytes are available. Setting |margin_mb| to -1
// turns off low memory notification.
-BASE_EXPORT bool AdjustLowMemoryMargin(int64_t margin_mb);
+bool AdjustLowMemoryMargin(int64_t margin_mb);
#endif // SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
diff --git a/chromium/sandbox/linux/suid/process_util_linux.c b/chromium/sandbox/linux/suid/process_util_linux.c
index 78c27ef5075..8d9a53c3a41 100644
--- a/chromium/sandbox/linux/suid/process_util_linux.c
+++ b/chromium/sandbox/linux/suid/process_util_linux.c
@@ -5,9 +5,12 @@
// The following is the C version of code from base/process_utils_linux.cc.
// We shouldn't link against C++ code in a setuid binary.
-#define _GNU_SOURCE // needed for O_DIRECTORY
+// Needed for O_DIRECTORY, must be defined before fcntl.h is included
+// (and it can be included earlier than the explicit #include below
+// in some versions of glibc).
+#define _GNU_SOURCE
-#include "process_util.h"
+#include "sandbox/linux/suid/process_util.h"
#include <fcntl.h>
#include <inttypes.h>
@@ -71,5 +74,5 @@ bool AdjustOOMScore(pid_t process, int score) {
ssize_t bytes_written = write(fd, buf, len);
close(fd);
- return (bytes_written == len);
+ return (bytes_written == (ssize_t)len);
}
diff --git a/chromium/sandbox/linux/suid/sandbox.c b/chromium/sandbox/linux/suid/sandbox.c
index 238a647abe9..7410b71c315 100644
--- a/chromium/sandbox/linux/suid/sandbox.c
+++ b/chromium/sandbox/linux/suid/sandbox.c
@@ -4,7 +4,7 @@
// http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
-#include "common/sandbox.h"
+#include "sandbox/linux/suid/common/sandbox.h"
#define _GNU_SOURCE
#include <asm/unistd.h>
@@ -29,9 +29,9 @@
#include <sys/wait.h>
#include <unistd.h>
-#include "linux_util.h"
-#include "process_util.h"
-#include "common/suid_unsafe_environment_variables.h"
+#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
+#include "sandbox/linux/suid/linux_util.h"
+#include "sandbox/linux/suid/process_util.h"
#if !defined(CLONE_NEWPID)
#define CLONE_NEWPID 0x20000000
@@ -44,10 +44,10 @@ static bool DropRoot();
#define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x)
-static void FatalError(const char *msg, ...)
+static void FatalError(const char* msg, ...)
__attribute__((noreturn, format(printf, 1, 2)));
-static void FatalError(const char *msg, ...) {
+static void FatalError(const char* msg, ...) {
va_list ap;
va_start(ap, msg);
@@ -85,20 +85,18 @@ static bool SpawnChrootHelper() {
return false;
}
- char *safedir = NULL;
+ char* safedir = NULL;
struct stat sdir_stat;
- if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
+ if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) {
safedir = SAFE_DIR;
- else
- if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
- safedir = SAFE_DIR2;
- else {
- fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
- return false;
- }
+ } else if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) {
+ safedir = SAFE_DIR2;
+ } else {
+ fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
+ return false;
+ }
- const pid_t pid = syscall(
- __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
+ const pid_t pid = syscall(__NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
if (pid == -1) {
perror("clone");
@@ -214,7 +212,7 @@ static void WaitForChildAndExit(pid_t child_pid) {
}
int wait_ret =
- HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
+ HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
if (!wait_ret && reaped_child_info.si_pid == child_pid) {
if (reaped_child_info.si_code == CLD_EXITED) {
@@ -229,10 +227,7 @@ static void WaitForChildAndExit(pid_t child_pid) {
static bool MoveToNewNamespaces() {
// These are the sets of flags which we'll try, in order.
- const int kCloneExtraFlags[] = {
- CLONE_NEWPID | CLONE_NEWNET,
- CLONE_NEWPID,
- };
+ const int kCloneExtraFlags[] = {CLONE_NEWPID | CLONE_NEWNET, CLONE_NEWPID, };
// We need to close kZygoteIdFd before the child can continue. We use this
// socketpair to tell the child when to continue;
@@ -241,10 +236,10 @@ static bool MoveToNewNamespaces() {
FatalError("Failed to create a socketpair");
}
- for (size_t i = 0;
- i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
+ for (size_t i = 0; i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
i++) {
pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
+ const int clone_errno = errno;
if (pid > 0) {
if (!DropRoot()) {
@@ -296,8 +291,20 @@ static bool MoveToNewNamespaces() {
break;
}
+ // If EINVAL then the system doesn't support the requested flags, so
+ // continue to try a different set.
+ // On any other errno value the system *does* support these flags but
+ // something went wrong, hence we bail with an error message rather then
+ // provide less security.
if (errno != EINVAL) {
- perror("Failed to move to new PID namespace");
+ fprintf(stderr, "Failed to move to new namespace:");
+ if (kCloneExtraFlags[i] & CLONE_NEWPID) {
+ fprintf(stderr, " PID namespaces supported,");
+ }
+ if (kCloneExtraFlags[i] & CLONE_NEWNET) {
+ fprintf(stderr, " Network namespace supported,");
+ }
+ fprintf(stderr, " but failed: errno = %s\n", strerror(clone_errno));
return false;
}
}
@@ -373,7 +380,7 @@ bool CheckAndExportApiVersion() {
// Check the environment to see if a specific API version was requested.
// assume version 0 if none.
long api_number = -1;
- char *api_string = getenv(kSandboxEnvironmentApiRequest);
+ char* api_string = getenv(kSandboxEnvironmentApiRequest);
if (!api_string) {
api_number = 0;
} else {
@@ -386,20 +393,22 @@ bool CheckAndExportApiVersion() {
// Warn only for now.
if (api_number != kSUIDSandboxApiNumber) {
- fprintf(stderr, "The setuid sandbox provides API version %ld, "
- "but you need %ld\n"
- "Please read "
- "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment."
- "\n\n",
- kSUIDSandboxApiNumber,
- api_number);
+ fprintf(
+ stderr,
+ "The setuid sandbox provides API version %ld, "
+ "but you need %ld\n"
+ "Please read "
+ "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment."
+ "\n\n",
+ kSUIDSandboxApiNumber,
+ api_number);
}
// Export our version so that the sandboxed process can verify it did not
// use an old sandbox.
char version_string[64];
- snprintf(version_string, sizeof(version_string), "%ld",
- kSUIDSandboxApiNumber);
+ snprintf(
+ version_string, sizeof(version_string), "%ld", kSUIDSandboxApiNumber);
if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) {
perror("setenv");
return false;
@@ -408,7 +417,7 @@ bool CheckAndExportApiVersion() {
return true;
}
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
if (argc <= 1) {
if (argc <= 0) {
return 1;
@@ -463,9 +472,10 @@ int main(int argc, char **argv) {
endptr = NULL;
errno = 0;
score = strtol(argv[3], &endptr, 10);
- if (score == LONG_MAX || score == LONG_MIN ||
- !endptr || *endptr || errno != 0)
+ if (score == LONG_MAX || score == LONG_MIN || !endptr || *endptr ||
+ errno != 0) {
return 1;
+ }
return AdjustOOMScore(pid, score);
}
@@ -474,6 +484,13 @@ int main(int argc, char **argv) {
return 1;
}
+ if (geteuid() != 0) {
+ fprintf(stderr,
+ "The setuid sandbox is not running as root. Common causes:\n"
+ " * An unprivileged process using ptrace on it, like a debugger.\n"
+ " * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n");
+ }
+
if (!MoveToNewNamespaces())
return 1;
if (!SpawnChrootHelper())
diff --git a/chromium/sandbox/mac/BUILD.gn b/chromium/sandbox/mac/BUILD.gn
new file mode 100644
index 00000000000..e6151979d1a
--- /dev/null
+++ b/chromium/sandbox/mac/BUILD.gn
@@ -0,0 +1,72 @@
+# 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.
+
+component("sandbox") {
+ sources = [
+ "bootstrap_sandbox.cc",
+ "bootstrap_sandbox.h",
+ "launchd_interception_server.cc",
+ "launchd_interception_server.h",
+ "mach_message_server.cc",
+ "mach_message_server.h",
+ "os_compatibility.cc",
+ "os_compatibility.h",
+ "policy.cc",
+ "policy.h",
+ "xpc.h",
+ ]
+
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+ libs = [ "bsm" ]
+
+ deps = [
+ "//base",
+ ":generate_stubs",
+ ]
+}
+
+generate_stubs_script = "//tools/generate_stubs/generate_stubs.py"
+generate_stubs_header = "xpc_stubs_header.fragment"
+generate_stubs_sig_public = "xpc_stubs.sig"
+generate_stubs_sig_private = "xpc_private_stubs.sig"
+generate_stubs_project = "sandbox/mac"
+generate_stubs_output_stem = "xpc_stubs"
+
+action("generate_stubs") {
+ script = generate_stubs_script
+ sources = [ generate_stubs_sig_public, generate_stubs_sig_private ]
+ source_prereqs = [ generate_stubs_header ]
+ outputs = [
+ "$target_gen_dir/$generate_stubs_output_stem.cc",
+ "$target_gen_dir/$generate_stubs_output_stem.h",
+ ]
+ args = [
+ "-i", rebase_path(target_gen_dir, root_build_dir),
+ "-o", rebase_path(target_gen_dir, root_build_dir),
+ "-t", "posix_stubs",
+ "-e", rebase_path(generate_stubs_header, root_build_dir),
+ "-s", generate_stubs_output_stem,
+ "-p", generate_stubs_project,
+ ]
+ args += rebase_path(sources, root_build_dir)
+}
+
+test("sandbox_mac_unittests") {
+ sources = [
+ "bootstrap_sandbox_unittest.mm",
+ "policy_unittest.cc",
+ ]
+
+ libs = [
+ "CoreFoundation.framework",
+ "Foundation.framework",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//base",
+ "//base/test:run_all_unittests",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/sandbox/mac/OWNERS b/chromium/sandbox/mac/OWNERS
new file mode 100644
index 00000000000..163563f967d
--- /dev/null
+++ b/chromium/sandbox/mac/OWNERS
@@ -0,0 +1,2 @@
+mark@chromium.org
+rsesek@chromium.org
diff --git a/chromium/sandbox/mac/bootstrap_sandbox.cc b/chromium/sandbox/mac/bootstrap_sandbox.cc
new file mode 100644
index 00000000000..a90f570eb47
--- /dev/null
+++ b/chromium/sandbox/mac/bootstrap_sandbox.cc
@@ -0,0 +1,133 @@
+// 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/mac/bootstrap_sandbox.h"
+
+#include <servers/bootstrap.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "sandbox/mac/launchd_interception_server.h"
+
+namespace sandbox {
+
+const int kNotAPolicy = -1;
+
+// static
+scoped_ptr<BootstrapSandbox> BootstrapSandbox::Create() {
+ scoped_ptr<BootstrapSandbox> null; // Used for early returns.
+ scoped_ptr<BootstrapSandbox> sandbox(new BootstrapSandbox());
+ sandbox->server_.reset(new LaunchdInterceptionServer(sandbox.get()));
+
+ // Check in with launchd to get the receive right for the server that is
+ // published in the bootstrap namespace.
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_check_in(bootstrap_port,
+ sandbox->server_bootstrap_name().c_str(), &port);
+ if (kr != KERN_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr)
+ << "Failed to bootstrap_check_in the sandbox server.";
+ return null.Pass();
+ }
+ base::mac::ScopedMachReceiveRight scoped_port(port);
+
+ // Start the sandbox server.
+ if (sandbox->server_->Initialize(scoped_port.get()))
+ ignore_result(scoped_port.release()); // Transferred to server_.
+ else
+ return null.Pass();
+
+ return sandbox.Pass();
+}
+
+BootstrapSandbox::~BootstrapSandbox() {
+}
+
+void BootstrapSandbox::RegisterSandboxPolicy(
+ int sandbox_policy_id,
+ const BootstrapSandboxPolicy& policy) {
+ CHECK(IsPolicyValid(policy));
+ CHECK_GT(sandbox_policy_id, kNotAPolicy);
+ base::AutoLock lock(lock_);
+ DCHECK(policies_.find(sandbox_policy_id) == policies_.end());
+ policies_.insert(std::make_pair(sandbox_policy_id, policy));
+}
+
+void BootstrapSandbox::PrepareToForkWithPolicy(int sandbox_policy_id) {
+ base::AutoLock lock(lock_);
+
+ // Verify that this is a real policy.
+ CHECK(policies_.find(sandbox_policy_id) != policies_.end());
+ CHECK_EQ(kNotAPolicy, effective_policy_id_)
+ << "Cannot nest calls to PrepareToForkWithPolicy()";
+
+ // Store the policy for the process we're about to create.
+ effective_policy_id_ = sandbox_policy_id;
+}
+
+// TODO(rsesek): The |lock_| needs to be taken twice because
+// base::LaunchProcess handles both fork+exec, and holding the lock for the
+// duration would block servicing of other bootstrap messages. If a better
+// LaunchProcess existed (do arbitrary work without layering violations), this
+// could be avoided.
+
+void BootstrapSandbox::FinishedFork(base::ProcessHandle handle) {
+ base::AutoLock lock(lock_);
+
+ CHECK_NE(kNotAPolicy, effective_policy_id_)
+ << "Must PrepareToForkWithPolicy() before FinishedFork()";
+
+ // Apply the policy to the new process.
+ if (handle != base::kNullProcessHandle) {
+ const auto& existing_process = sandboxed_processes_.find(handle);
+ CHECK(existing_process == sandboxed_processes_.end());
+ sandboxed_processes_.insert(std::make_pair(handle, effective_policy_id_));
+ VLOG(3) << "Bootstrap sandbox enforced for pid " << handle;
+ }
+
+ effective_policy_id_ = kNotAPolicy;
+}
+
+void BootstrapSandbox::ChildDied(base::ProcessHandle handle) {
+ base::AutoLock lock(lock_);
+ const auto& it = sandboxed_processes_.find(handle);
+ if (it != sandboxed_processes_.end())
+ sandboxed_processes_.erase(it);
+}
+
+const BootstrapSandboxPolicy* BootstrapSandbox::PolicyForProcess(
+ pid_t pid) const {
+ base::AutoLock lock(lock_);
+ const auto& process = sandboxed_processes_.find(pid);
+
+ // The new child could send bootstrap requests before the parent calls
+ // FinishedFork().
+ int policy_id = effective_policy_id_;
+ if (process != sandboxed_processes_.end()) {
+ policy_id = process->second;
+ }
+
+ if (policy_id == kNotAPolicy)
+ return NULL;
+
+ return &policies_.find(policy_id)->second;
+}
+
+BootstrapSandbox::BootstrapSandbox()
+ : server_bootstrap_name_(
+ base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(),
+ getpid())),
+ real_bootstrap_port_(MACH_PORT_NULL),
+ effective_policy_id_(kNotAPolicy) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = task_get_special_port(
+ mach_task_self(), TASK_BOOTSTRAP_PORT, &port);
+ MACH_CHECK(kr == KERN_SUCCESS, kr);
+ real_bootstrap_port_.reset(port);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/bootstrap_sandbox.h b/chromium/sandbox/mac/bootstrap_sandbox.h
new file mode 100644
index 00000000000..dff7814d545
--- /dev/null
+++ b/chromium/sandbox/mac/bootstrap_sandbox.h
@@ -0,0 +1,114 @@
+// 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_MAC_BOOTSTRAP_SANDBOX_H_
+#define SANDBOX_MAC_BOOTSTRAP_SANDBOX_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <string>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/process_handle.h"
+#include "base/synchronization/lock.h"
+#include "sandbox/mac/policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+class LaunchdInterceptionServer;
+
+// The BootstrapSandbox is a second-layer sandbox for Mac. It is used to limit
+// the bootstrap namespace attack surface of child processes. The parent
+// process creates an instance of this class and registers policies that it
+// can enforce on its children.
+//
+// With this sandbox, the parent process must replace the bootstrap port prior
+// to the sandboxed target's execution. This should be done by setting the
+// base::LaunchOptions.replacement_bootstrap_name to the
+// server_bootstrap_name() of this class. Requests from the child that would
+// normally go to launchd are filtered based on the specified per-process
+// policies. If a request is permitted by the policy, it is forwarded on to
+// launchd for servicing. If it is not, then the sandbox will reply with a
+// primitive that does not grant additional capabilities to the receiver.
+//
+// Clients that which to use the sandbox must inform it of the creation and
+// death of child processes for which the sandbox should be enforced. The
+// client of the sandbox is intended to be an unsandboxed parent process that
+// fork()s sandboxed (and other unsandboxed) child processes.
+//
+// When the parent is ready to fork a new child process with this sandbox
+// being enforced, it should use the pair of methods PrepareToForkWithPolicy()
+// and FinishedFork(), and call fork() between them. The first method will
+// set the policy for the new process, and the second will finialize the
+// association between the process ID and sandbox policy ID.
+//
+// All methods of this class may be called from any thread, but
+// PrepareToForkWithPolicy() and FinishedFork() must be non-nested and balanced.
+class SANDBOX_EXPORT BootstrapSandbox {
+ public:
+ // Creates a new sandbox manager. Returns NULL on failure.
+ static scoped_ptr<BootstrapSandbox> Create();
+
+ ~BootstrapSandbox();
+
+ // Registers a bootstrap policy associated it with an identifier. The
+ // |sandbox_policy_id| must be greater than 0.
+ void RegisterSandboxPolicy(int sandbox_policy_id,
+ const BootstrapSandboxPolicy& policy);
+
+ // Called in the parent prior to fork()ing a child. The policy registered
+ // to |sandbox_policy_id| will be enforced on the new child. This must be
+ // followed by a call to FinishedFork().
+ void PrepareToForkWithPolicy(int sandbox_policy_id);
+
+ // Called in the parent after fork()ing a child. It records the |handle|
+ // and associates it with the specified-above |sandbox_policy_id|.
+ // If fork() failed and a new child was not created, pass kNullProcessHandle.
+ void FinishedFork(base::ProcessHandle handle);
+
+ // Called in the parent when a process has died. It cleans up the references
+ // to the process.
+ void ChildDied(base::ProcessHandle handle);
+
+ // Looks up the policy for a given process ID. If no policy is associated
+ // with the |pid|, this returns NULL.
+ const BootstrapSandboxPolicy* PolicyForProcess(pid_t pid) const;
+
+ std::string server_bootstrap_name() const { return server_bootstrap_name_; }
+ mach_port_t real_bootstrap_port() const { return real_bootstrap_port_; }
+
+ private:
+ BootstrapSandbox();
+
+ // A Mach IPC message server that is used to intercept and filter bootstrap
+ // requests.
+ scoped_ptr<LaunchdInterceptionServer> server_;
+
+ // The name in the system bootstrap server by which the |server_|'s port
+ // is known.
+ const std::string server_bootstrap_name_;
+
+ // The original bootstrap port of the process, which is connected to the
+ // real launchd server.
+ base::mac::ScopedMachSendRight real_bootstrap_port_;
+
+ // The |lock_| protects all the following variables.
+ mutable base::Lock lock_;
+
+ // The sandbox_policy_id that will be enforced for the new child.
+ int effective_policy_id_;
+
+ // All the policies that have been registered with this sandbox manager.
+ std::map<int, const BootstrapSandboxPolicy> policies_;
+
+ // The association between process ID and sandbox policy ID.
+ std::map<base::ProcessHandle, int> sandboxed_processes_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_BOOTSTRAP_SANDBOX_H_
diff --git a/chromium/sandbox/mac/bootstrap_sandbox_unittest.mm b/chromium/sandbox/mac/bootstrap_sandbox_unittest.mm
new file mode 100644
index 00000000000..85f627f10a3
--- /dev/null
+++ b/chromium/sandbox/mac/bootstrap_sandbox_unittest.mm
@@ -0,0 +1,417 @@
+// 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/mac/bootstrap_sandbox.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#import <Foundation/Foundation.h>
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
+
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/process/kill.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#import "testing/gtest_mac.h"
+#include "testing/multiprocess_func_list.h"
+
+NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
+
+@interface DistributedNotificationObserver : NSObject {
+ @private
+ int receivedCount_;
+ base::scoped_nsobject<NSString> object_;
+}
+- (int)receivedCount;
+- (NSString*)object;
+- (void)waitForNotification;
+@end
+
+@implementation DistributedNotificationObserver
+- (id)init {
+ if ((self = [super init])) {
+ [[NSDistributedNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(observeNotification:)
+ name:kTestNotification
+ object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSDistributedNotificationCenter defaultCenter]
+ removeObserver:self
+ name:kTestNotification
+ object:nil];
+ [super dealloc];
+}
+
+- (int)receivedCount {
+ return receivedCount_;
+}
+
+- (NSString*)object {
+ return object_.get();
+}
+
+- (void)waitForNotification {
+ object_.reset();
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode,
+ TestTimeouts::action_timeout().InSeconds(), false);
+}
+
+- (void)observeNotification:(NSNotification*)notification {
+ ++receivedCount_;
+ object_.reset([[notification object] copy]);
+ CFRunLoopStop(CFRunLoopGetCurrent());
+}
+@end
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace sandbox {
+
+class BootstrapSandboxTest : public base::MultiProcessTest {
+ public:
+ virtual void SetUp() OVERRIDE {
+ base::MultiProcessTest::SetUp();
+
+ sandbox_ = BootstrapSandbox::Create();
+ ASSERT_TRUE(sandbox_.get());
+ }
+
+ BootstrapSandboxPolicy BaselinePolicy() {
+ BootstrapSandboxPolicy policy;
+ if (base::mac::IsOSSnowLeopard())
+ policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
+ return policy;
+ }
+
+ void RunChildWithPolicy(int policy_id,
+ const char* child_name,
+ base::ProcessHandle* out_pid) {
+ sandbox_->PrepareToForkWithPolicy(policy_id);
+ base::LaunchOptions options;
+ options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
+ base::ProcessHandle pid = SpawnChildWithOptions(child_name, options);
+ ASSERT_GT(pid, 0);
+ sandbox_->FinishedFork(pid);
+ int code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(pid, &code));
+ EXPECT_EQ(0, code);
+ if (out_pid)
+ *out_pid = pid;
+ }
+
+ protected:
+ scoped_ptr<BootstrapSandbox> sandbox_;
+};
+
+const char kNotificationTestMain[] = "PostNotification";
+
+// Run the test without the sandbox.
+TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ base::ProcessHandle pid = SpawnChild(kNotificationTestMain);
+ ASSERT_GT(pid, 0);
+ int code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(pid, &code));
+ EXPECT_EQ(0, code);
+
+ [observer waitForNotification];
+ EXPECT_EQ(1, [observer receivedCount]);
+ EXPECT_EQ(pid, [[observer object] intValue]);
+}
+
+// Run the test with the sandbox enabled without notifications on the policy
+// whitelist.
+TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
+ RunChildWithPolicy(1, kNotificationTestMain, NULL);
+
+ [observer waitForNotification];
+ EXPECT_EQ(0, [observer receivedCount]);
+ EXPECT_EQ(nil, [observer object]);
+}
+
+// Run the test with notifications permitted.
+TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ // 10.9:
+ policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
+ policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
+ // 10.6:
+ policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW);
+ policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW);
+ sandbox_->RegisterSandboxPolicy(2, policy);
+
+ base::ProcessHandle pid;
+ RunChildWithPolicy(2, kNotificationTestMain, &pid);
+
+ [observer waitForNotification];
+ EXPECT_EQ(1, [observer receivedCount]);
+ EXPECT_EQ(pid, [[observer object] intValue]);
+}
+
+MULTIPROCESS_TEST_MAIN(PostNotification) {
+ [[NSDistributedNotificationCenter defaultCenter]
+ postNotificationName:kTestNotification
+ object:[NSString stringWithFormat:@"%d", getpid()]];
+ return 0;
+}
+
+const char kTestServer[] = "org.chromium.test_bootstrap_server";
+
+TEST_F(BootstrapSandboxTest, PolicyDenyError) {
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
+ sandbox_->RegisterSandboxPolicy(1, policy);
+
+ RunChildWithPolicy(1, "PolicyDenyError", NULL);
+}
+
+MULTIPROCESS_TEST_MAIN(PolicyDenyError) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
+ &port);
+ CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
+ CHECK(port == MACH_PORT_NULL);
+
+ kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server",
+ &port);
+ CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
+ CHECK(port == MACH_PORT_NULL);
+
+ return 0;
+}
+
+TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
+ sandbox_->RegisterSandboxPolicy(1, policy);
+
+ RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
+}
+
+MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
+ &port);
+ CHECK_EQ(KERN_SUCCESS, kr);
+ CHECK(port != MACH_PORT_NULL);
+ return 0;
+}
+
+struct SubstitutePortAckSend {
+ mach_msg_header_t header;
+ char buf[32];
+};
+
+struct SubstitutePortAckRecv : public SubstitutePortAckSend {
+ mach_msg_trailer_t trailer;
+};
+
+const char kSubstituteAck[] = "Hello, this is doge!";
+
+TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
+ mach_port_t task = mach_task_self();
+
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port(port);
+
+ mach_port_urefs_t send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(0u, send_rights);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules[kTestServer] = Rule(port);
+ sandbox_->RegisterSandboxPolicy(1, policy);
+
+ RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
+
+ struct SubstitutePortAckRecv msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_local_port = port;
+ kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
+ msg.header.msgh_size, port,
+ TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
+ EXPECT_EQ(KERN_SUCCESS, kr);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
+}
+
+MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port);
+ CHECK_EQ(KERN_SUCCESS, kr);
+ CHECK(port != MACH_PORT_NULL);
+
+ struct SubstitutePortAckSend msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_remote_port = port;
+ msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
+ strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
+
+ CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
+
+ return 0;
+}
+
+TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
+ mach_port_t task = mach_task_self();
+
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port_recv(port);
+
+ mach_port_urefs_t send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(0u, send_rights);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ mach_port_t bp;
+ ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
+ base::mac::ScopedMachSendRight scoped_bp(bp);
+
+ char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess";
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ kern_return_t kr = bootstrap_register(bp, service_name, port);
+#pragma GCC diagnostic pop
+ EXPECT_EQ(KERN_SUCCESS, kr);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ mach_port_t service_port;
+ EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port));
+ base::mac::ScopedMachSendRight scoped_service_port(service_port);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
+ // process cache.
+ if (base::mac::IsOSSnowLeopard())
+ EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
+ else
+ EXPECT_EQ(2u, send_rights);
+}
+
+const char kDefaultRuleTestAllow[] =
+ "org.chromium.sandbox.test.DefaultRuleAllow";
+const char kDefaultRuleTestDeny[] =
+ "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
+
+TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
+ mach_port_t task = mach_task_self();
+
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port_recv(port);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = Rule(POLICY_ALLOW);
+ policy.rules[kDefaultRuleTestAllow] = Rule(port);
+ policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR);
+ sandbox_->RegisterSandboxPolicy(3, policy);
+
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ int pid = 0;
+ RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
+ EXPECT_GT(pid, 0);
+
+ [observer waitForNotification];
+ EXPECT_EQ(1, [observer receivedCount]);
+ EXPECT_EQ(pid, [[observer object] intValue]);
+
+ struct SubstitutePortAckRecv msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_local_port = port;
+ kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
+ msg.header.msgh_size, port,
+ TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
+ EXPECT_EQ(KERN_SUCCESS, kr);
+
+ EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
+}
+
+MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
+ [[NSDistributedNotificationCenter defaultCenter]
+ postNotificationName:kTestNotification
+ object:[NSString stringWithFormat:@"%d", getpid()]];
+
+ mach_port_t port = MACH_PORT_NULL;
+ CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(kDefaultRuleTestDeny), &port));
+ CHECK(port == MACH_PORT_NULL);
+
+ CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(kDefaultRuleTestAllow), &port));
+ CHECK(port != MACH_PORT_NULL);
+
+ struct SubstitutePortAckSend msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_remote_port = port;
+ msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
+ strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
+
+ CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
+
+ return 0;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/launchd_interception_server.cc b/chromium/sandbox/mac/launchd_interception_server.cc
new file mode 100644
index 00000000000..c3d6eaac579
--- /dev/null
+++ b/chromium/sandbox/mac/launchd_interception_server.cc
@@ -0,0 +1,153 @@
+// 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/mac/launchd_interception_server.h"
+
+#include <servers/bootstrap.h>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "sandbox/mac/bootstrap_sandbox.h"
+
+namespace sandbox {
+
+// The buffer size for all launchd messages. This comes from
+// sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
+// is larger than the __ReplyUnion.
+const mach_msg_size_t kBufferSize = 2096;
+
+LaunchdInterceptionServer::LaunchdInterceptionServer(
+ const BootstrapSandbox* sandbox)
+ : sandbox_(sandbox),
+ sandbox_port_(MACH_PORT_NULL),
+ compat_shim_(GetLaunchdCompatibilityShim()) {
+}
+
+LaunchdInterceptionServer::~LaunchdInterceptionServer() {
+}
+
+bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) {
+ mach_port_t task = mach_task_self();
+ kern_return_t kr;
+
+ // Allocate the dummy sandbox port.
+ mach_port_t port;
+ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
+ KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port.";
+ return false;
+ }
+ sandbox_port_.reset(port);
+ if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_,
+ MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right.";
+ return false;
+ }
+ sandbox_send_port_.reset(sandbox_port_);
+
+ message_server_.reset(
+ new MachMessageServer(this, server_receive_right, kBufferSize));
+ return message_server_->Initialize();
+}
+
+void LaunchdInterceptionServer::DemuxMessage(mach_msg_header_t* request,
+ mach_msg_header_t* reply) {
+ VLOG(3) << "Incoming message #" << request->msgh_id;
+
+ pid_t sender_pid = message_server_->GetMessageSenderPID(request);
+ const BootstrapSandboxPolicy* policy =
+ sandbox_->PolicyForProcess(sender_pid);
+ if (policy == NULL) {
+ // No sandbox policy is in place for the sender of this message, which
+ // means it came from the unknown. Reject it.
+ VLOG(3) << "Message from unknown pid " << sender_pid << " rejected.";
+ message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
+ return;
+ }
+
+ if (request->msgh_id == compat_shim_.msg_id_look_up2) {
+ // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
+ // over the bootstrap namespace.
+ HandleLookUp(request, reply, policy);
+ } else if (request->msgh_id == compat_shim_.msg_id_swap_integer) {
+ // Ensure that any vproc_swap_integer requests are safe.
+ HandleSwapInteger(request, reply);
+ } else {
+ // All other messages are not permitted.
+ VLOG(1) << "Rejecting unhandled message #" << request->msgh_id;
+ message_server_->RejectMessage(reply, MIG_REMOTE_ERROR);
+ }
+}
+
+void LaunchdInterceptionServer::HandleLookUp(
+ mach_msg_header_t* request,
+ mach_msg_header_t* reply,
+ const BootstrapSandboxPolicy* policy) {
+ const std::string request_service_name(
+ compat_shim_.look_up2_get_request_name(request));
+ VLOG(2) << "Incoming look_up2 request for " << request_service_name;
+
+ // Find the Rule for this service. If a named rule is not found, use the
+ // default specified by the policy.
+ const BootstrapSandboxPolicy::NamedRules::const_iterator it =
+ policy->rules.find(request_service_name);
+ Rule rule(policy->default_rule);
+ if (it != policy->rules.end())
+ rule = it->second;
+
+ if (rule.result == POLICY_ALLOW) {
+ // This service is explicitly allowed, so this message will not be
+ // intercepted by the sandbox.
+ VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name;
+ ForwardMessage(request);
+ } else if (rule.result == POLICY_DENY_ERROR) {
+ // The child is not permitted to look up this service. Send a MIG error
+ // reply to the client. Returning a NULL or unserviced port for a look up
+ // can cause clients to crash or hang.
+ VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name;
+ message_server_->RejectMessage(reply, BOOTSTRAP_UNKNOWN_SERVICE);
+ } else if (rule.result == POLICY_DENY_DUMMY_PORT ||
+ rule.result == POLICY_SUBSTITUTE_PORT) {
+ // The policy result is to deny access to the real service port, replying
+ // with a sandboxed port in its stead. Use either the dummy sandbox_port_
+ // or the one specified in the policy.
+ VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
+ << request_service_name;
+
+ mach_port_t result_port;
+ if (rule.result == POLICY_DENY_DUMMY_PORT)
+ result_port = sandbox_port_.get();
+ else
+ result_port = rule.substitute_port;
+
+ compat_shim_.look_up2_fill_reply(reply, result_port);
+ // If the message was sent successfully, clear the result_port out of the
+ // message so that it is not destroyed at the end of ReceiveMessage. The
+ // above-inserted right has been moved out of the process, and destroying
+ // the message will unref yet another right.
+ if (message_server_->SendReply(reply))
+ compat_shim_.look_up2_fill_reply(reply, MACH_PORT_NULL);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void LaunchdInterceptionServer::HandleSwapInteger(mach_msg_header_t* request,
+ mach_msg_header_t* reply) {
+ // Only allow getting information out of launchd. Do not allow setting
+ // values. Two commonly observed values that are retrieved are
+ // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED.
+ if (compat_shim_.swap_integer_is_get_only(request)) {
+ VLOG(2) << "Forwarding vproc swap_integer message.";
+ ForwardMessage(request);
+ } else {
+ VLOG(2) << "Rejecting non-read-only swap_integer message.";
+ message_server_->RejectMessage(reply, BOOTSTRAP_NOT_PRIVILEGED);
+ }
+}
+void LaunchdInterceptionServer::ForwardMessage(mach_msg_header_t* request) {
+ message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/launchd_interception_server.h b/chromium/sandbox/mac/launchd_interception_server.h
new file mode 100644
index 00000000000..ec25be16133
--- /dev/null
+++ b/chromium/sandbox/mac/launchd_interception_server.h
@@ -0,0 +1,78 @@
+// 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_MAC_LAUNCHD_INTERCEPTION_SERVER_H_
+#define SANDBOX_MAC_LAUNCHD_INTERCEPTION_SERVER_H_
+
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/mac/mach_message_server.h"
+#include "sandbox/mac/os_compatibility.h"
+
+namespace sandbox {
+
+class BootstrapSandbox;
+struct BootstrapSandboxPolicy;
+
+// This class is used to run a Mach IPC message server. This server can
+// hold the receive right for a bootstrap_port of a process, and it filters
+// a subset of the launchd/bootstrap IPC call set for sandboxing. It permits
+// or rejects requests based on the per-process policy specified in the
+// BootstrapSandbox.
+class LaunchdInterceptionServer : public MessageDemuxer {
+ public:
+ explicit LaunchdInterceptionServer(const BootstrapSandbox* sandbox);
+ virtual ~LaunchdInterceptionServer();
+
+ // Initializes the class and starts running the message server. If the
+ // |server_receive_right| is non-NULL, this class will take ownership of
+ // the receive right and intercept messages sent to that port.
+ bool Initialize(mach_port_t server_receive_right);
+
+ // MessageDemuxer:
+ virtual void DemuxMessage(mach_msg_header_t* request,
+ mach_msg_header_t* reply) OVERRIDE;
+
+ mach_port_t server_port() const { return message_server_->server_port(); }
+
+ private:
+ // Given a look_up2 request message, this looks up the appropriate sandbox
+ // policy for the service name then formulates and sends the reply message.
+ void HandleLookUp(mach_msg_header_t* request,
+ mach_msg_header_t* reply,
+ const BootstrapSandboxPolicy* policy);
+
+ // Given a swap_integer request message, this verifies that it is safe, and
+ // if so, forwards it on to launchd for servicing. If the request is unsafe,
+ // it replies with an error.
+ void HandleSwapInteger(mach_msg_header_t* request,
+ mach_msg_header_t* reply);
+
+ // Forwards the original |request| on to real bootstrap server for handling.
+ void ForwardMessage(mach_msg_header_t* request);
+
+ // The sandbox for which this message server is running.
+ const BootstrapSandbox* sandbox_;
+
+ // The Mach IPC server.
+ scoped_ptr<MachMessageServer> message_server_;
+
+ // The Mach port handed out in reply to denied look up requests. All denied
+ // requests share the same port, though nothing reads messages from it.
+ base::mac::ScopedMachReceiveRight sandbox_port_;
+ // The send right for the above |sandbox_port_|, used with
+ // MACH_MSG_TYPE_COPY_SEND when handing out references to the dummy port.
+ base::mac::ScopedMachSendRight sandbox_send_port_;
+
+ // The compatibility shim that handles differences in message header IDs and
+ // request/reply structures between different OS X versions.
+ const LaunchdCompatibilityShim compat_shim_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_LAUNCHD_INTERCEPTION_SERVER_H_
diff --git a/chromium/sandbox/mac/mach_message_server.cc b/chromium/sandbox/mac/mach_message_server.cc
new file mode 100644
index 00000000000..555ee0f494e
--- /dev/null
+++ b/chromium/sandbox/mac/mach_message_server.cc
@@ -0,0 +1,184 @@
+// 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/mac/mach_message_server.h"
+
+#include <bsm/libbsm.h>
+#include <servers/bootstrap.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+
+namespace sandbox {
+
+MachMessageServer::MachMessageServer(
+ MessageDemuxer* demuxer,
+ mach_port_t server_receive_right,
+ mach_msg_size_t buffer_size)
+ : demuxer_(demuxer),
+ server_port_(server_receive_right),
+ server_queue_(NULL),
+ server_source_(NULL),
+ buffer_size_(
+ mach_vm_round_page(buffer_size + sizeof(mach_msg_audit_trailer_t))),
+ did_forward_message_(false) {
+ DCHECK(demuxer_);
+}
+
+MachMessageServer::~MachMessageServer() {
+ if (server_source_)
+ dispatch_release(server_source_);
+ if (server_queue_)
+ dispatch_release(server_queue_);
+}
+
+bool MachMessageServer::Initialize() {
+ mach_port_t task = mach_task_self();
+ kern_return_t kr;
+
+ // Allocate a port for use as a new server port if one was not passed to the
+ // constructor.
+ if (!server_port_.is_valid()) {
+ mach_port_t port;
+ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
+ KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate new server port.";
+ return false;
+ }
+ server_port_.reset(port);
+ }
+
+ // Allocate the message request and reply buffers.
+ const int kMachMsgMemoryFlags = VM_MAKE_TAG(VM_MEMORY_MACH_MSG) |
+ VM_FLAGS_ANYWHERE;
+ vm_address_t buffer = 0;
+
+ kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate request buffer.";
+ return false;
+ }
+ request_buffer_.reset(buffer, buffer_size_);
+
+ kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate reply buffer.";
+ return false;
+ }
+ reply_buffer_.reset(buffer, buffer_size_);
+
+ // Set up the dispatch queue to service the bootstrap port.
+ // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL means
+ // the same thing but is not symbolically clear.
+ std::string label = base::StringPrintf(
+ "org.chromium.sandbox.MachMessageServer.%p", demuxer_);
+ server_queue_ = dispatch_queue_create(label.c_str(), NULL);
+ server_source_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
+ server_port_.get(), 0, server_queue_);
+ dispatch_source_set_event_handler(server_source_, ^{ ReceiveMessage(); });
+ dispatch_resume(server_source_);
+
+ return true;
+}
+
+pid_t MachMessageServer::GetMessageSenderPID(mach_msg_header_t* request) {
+ // Get the PID of the task that sent this request. This requires getting at
+ // the trailer of the message, from the header.
+ mach_msg_audit_trailer_t* trailer =
+ reinterpret_cast<mach_msg_audit_trailer_t*>(
+ reinterpret_cast<vm_address_t>(request) +
+ round_msg(request->msgh_size));
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t sender_pid;
+ audit_token_to_au32(trailer->msgh_audit,
+ NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
+ return sender_pid;
+}
+
+bool MachMessageServer::SendReply(mach_msg_header_t* reply) {
+ kern_return_t kr = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+ << "Unable to send intercepted reply message.";
+ return kr == KERN_SUCCESS;
+}
+
+void MachMessageServer::ForwardMessage(mach_msg_header_t* request,
+ mach_port_t destination) {
+ request->msgh_local_port = request->msgh_remote_port;
+ request->msgh_remote_port = destination;
+ // Preserve the msgh_bits that do not deal with the local and remote ports.
+ request->msgh_bits = (request->msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ kern_return_t kr = mach_msg_send(request);
+ if (kr == KERN_SUCCESS) {
+ did_forward_message_ = true;
+ } else {
+ MACH_LOG(ERROR, kr) << "Unable to forward message to the real launchd.";
+ }
+}
+
+void MachMessageServer::RejectMessage(mach_msg_header_t* reply,
+ int error_code) {
+ mig_reply_error_t* error_reply = reinterpret_cast<mig_reply_error_t*>(reply);
+ error_reply->Head.msgh_size = sizeof(mig_reply_error_t);
+ error_reply->Head.msgh_bits =
+ MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ error_reply->NDR = NDR_record;
+ error_reply->RetCode = error_code;
+ SendReply(&error_reply->Head);
+}
+
+void MachMessageServer::ReceiveMessage() {
+ const mach_msg_options_t kRcvOptions = MACH_RCV_MSG |
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
+ mach_msg_header_t* request =
+ reinterpret_cast<mach_msg_header_t*>(request_buffer_.address());
+ mach_msg_header_t* reply =
+ reinterpret_cast<mach_msg_header_t*>(reply_buffer_.address());
+
+ // Zero out the buffers from handling any previous message.
+ bzero(request, buffer_size_);
+ bzero(reply, buffer_size_);
+ did_forward_message_ = false;
+
+ // A Mach message server-once. The system library to run a message server
+ // cannot be used here, because some requests are conditionally forwarded
+ // to another server.
+ kern_return_t kr = mach_msg(request, kRcvOptions, 0, buffer_size_,
+ server_port_.get(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Unable to receive message.";
+ return;
+ }
+
+ // Set up a reply message in case it will be used.
+ reply->msgh_bits = MACH_MSGH_BITS_REMOTE(reply->msgh_bits);
+ // Since mach_msg will automatically swap the request and reply ports,
+ // undo that.
+ reply->msgh_remote_port = request->msgh_remote_port;
+ reply->msgh_local_port = MACH_PORT_NULL;
+ // MIG servers simply add 100 to the request ID to generate the reply ID.
+ reply->msgh_id = request->msgh_id + 100;
+
+ // Process the message.
+ demuxer_->DemuxMessage(request, reply);
+
+ // Free any descriptors in the message body. If the message was forwarded,
+ // any descriptors would have been moved out of the process on send. If the
+ // forwarded message was sent from the process hosting this sandbox server,
+ // destroying the message could also destroy rights held outside the scope of
+ // this message server.
+ if (!did_forward_message_) {
+ mach_msg_destroy(request);
+ mach_msg_destroy(reply);
+ }
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/mach_message_server.h b/chromium/sandbox/mac/mach_message_server.h
new file mode 100644
index 00000000000..5c05c39b414
--- /dev/null
+++ b/chromium/sandbox/mac/mach_message_server.h
@@ -0,0 +1,95 @@
+// 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_MAC_MACH_MESSAGE_SERVER_H_
+#define SANDBOX_MAC_MACH_MESSAGE_SERVER_H_
+
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_mach_vm.h"
+
+namespace sandbox {
+
+// A delegate interface for MachMessageServer that handles processing of
+// incoming intercepted IPC messages.
+class MessageDemuxer {
+ public:
+ // Handle a |request| message and optionally create a |reply|. Both message
+ // objects are owned by the server. Use the server's methods to send a
+ // reply message.
+ virtual void DemuxMessage(mach_msg_header_t* request,
+ mach_msg_header_t* reply) = 0;
+
+ protected:
+ virtual ~MessageDemuxer() {}
+};
+
+// A Mach message server that operates a receive port. Messages are received
+// and then passed to the MessageDemuxer for handling. The Demuxer
+// can use the server class to send a reply, forward the message to a
+// different port, or reply to the message with a MIG error.
+class MachMessageServer {
+ public:
+ // Creates a new Mach message server that will send messages to |demuxer|
+ // for handling. If the |server_receive_right| is non-NULL, this class will
+ // take ownership of the port and it will be used to receive messages.
+ // Otherwise the server will create a new receive right.
+ // The maximum size of messages is specified by |buffer_size|.
+ MachMessageServer(MessageDemuxer* demuxer,
+ mach_port_t server_receive_right,
+ mach_msg_size_t buffer_size);
+ ~MachMessageServer();
+
+ // Initializes the class and starts running the message server. If this
+ // returns false, no other methods may be called on this class.
+ bool Initialize();
+
+ // Given a received request message, returns the PID of the sending process.
+ pid_t GetMessageSenderPID(mach_msg_header_t* request);
+
+ // Sends a reply message. Returns true if the message was sent successfully.
+ bool SendReply(mach_msg_header_t* reply);
+
+ // Forwards the original |request| to the |destination| for handling.
+ void ForwardMessage(mach_msg_header_t* request, mach_port_t destination);
+
+ // Replies to the message with the specified |error_code| as a MIG
+ // error_reply RetCode.
+ void RejectMessage(mach_msg_header_t* reply, int error_code);
+
+ mach_port_t server_port() const { return server_port_.get(); }
+
+ private:
+ // Event handler for the |server_source_| that reads a message from the queue
+ // and processes it.
+ void ReceiveMessage();
+
+ // The demuxer delegate. Weak.
+ MessageDemuxer* demuxer_;
+
+ // The Mach port on which the server is receiving requests.
+ base::mac::ScopedMachReceiveRight server_port_;
+
+ // The dispatch queue used to service the server_source_.
+ dispatch_queue_t server_queue_;
+
+ // A MACH_RECV dispatch source for the server_port_.
+ dispatch_source_t server_source_;
+
+ // The size of the two message buffers below.
+ const mach_msg_size_t buffer_size_;
+
+ // Request and reply buffers used in ReceiveMessage.
+ base::mac::ScopedMachVM request_buffer_;
+ base::mac::ScopedMachVM reply_buffer_;
+
+ // Whether or not ForwardMessage() was called during ReceiveMessage().
+ bool did_forward_message_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_MACH_MESSAGE_SERVER_H_
diff --git a/chromium/sandbox/mac/os_compatibility.cc b/chromium/sandbox/mac/os_compatibility.cc
new file mode 100644
index 00000000000..4485006edb5
--- /dev/null
+++ b/chromium/sandbox/mac/os_compatibility.cc
@@ -0,0 +1,127 @@
+// 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/mac/os_compatibility.h"
+
+#include <servers/bootstrap.h>
+#include <unistd.h>
+
+#include "base/mac/mac_util.h"
+
+namespace sandbox {
+
+namespace {
+
+// Verified from launchd-329.3.3 (10.6.8).
+struct look_up2_request_10_6 {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ name_t servicename;
+ pid_t targetpid;
+ uint64_t flags;
+};
+
+struct look_up2_reply_10_6 {
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t service_port;
+};
+
+// Verified from:
+// launchd-392.39 (10.7.5)
+// launchd-442.26.2 (10.8.5)
+// launchd-842.1.4 (10.9.0)
+struct look_up2_request_10_7 {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ name_t servicename;
+ pid_t targetpid;
+ uuid_t instanceid;
+ uint64_t flags;
+};
+
+// look_up2_reply_10_7 is the same as the 10_6 version.
+
+// Verified from:
+// launchd-329.3.3 (10.6.8)
+// launchd-392.39 (10.7.5)
+// launchd-442.26.2 (10.8.5)
+// launchd-842.1.4 (10.9.0)
+typedef int vproc_gsk_t; // Defined as an enum in liblaunch/vproc_priv.h.
+struct swap_integer_request_10_6 {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ vproc_gsk_t inkey;
+ vproc_gsk_t outkey;
+ int64_t inval;
+};
+
+// TODO(rsesek): Libc provides strnlen() starting in 10.7.
+size_t strnlen(const char* str, size_t maxlen) {
+ size_t len = 0;
+ for (; len < maxlen; ++len, ++str) {
+ if (*str == '\0')
+ break;
+ }
+ return len;
+}
+
+template <typename R>
+std::string LaunchdLookUp2GetRequestName(const mach_msg_header_t* header) {
+ DCHECK_EQ(sizeof(R), header->msgh_size);
+ const R* request = reinterpret_cast<const R*>(header);
+ // Make sure the name is properly NUL-terminated.
+ const size_t name_length =
+ strnlen(request->servicename, BOOTSTRAP_MAX_NAME_LEN);
+ std::string name = std::string(request->servicename, name_length);
+ return name;
+}
+
+template <typename R>
+void LaunchdLookUp2FillReply(mach_msg_header_t* header, mach_port_t port) {
+ R* reply = reinterpret_cast<R*>(header);
+ reply->Head.msgh_size = sizeof(R);
+ reply->Head.msgh_bits =
+ MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
+ MACH_MSGH_BITS_COMPLEX;
+ reply->msgh_body.msgh_descriptor_count = 1;
+ reply->service_port.name = port;
+ reply->service_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ reply->service_port.type = MACH_MSG_PORT_DESCRIPTOR;
+}
+
+template <typename R>
+bool LaunchdSwapIntegerIsGetOnly(const mach_msg_header_t* header) {
+ const R* request = reinterpret_cast<const R*>(header);
+ return request->inkey == 0 && request->inval == 0 && request->outkey != 0;
+}
+
+} // namespace
+
+const LaunchdCompatibilityShim GetLaunchdCompatibilityShim() {
+ LaunchdCompatibilityShim shim = {
+ .msg_id_look_up2 = 404,
+ .msg_id_swap_integer = 416,
+ .look_up2_fill_reply = &LaunchdLookUp2FillReply<look_up2_reply_10_6>,
+ .swap_integer_is_get_only =
+ &LaunchdSwapIntegerIsGetOnly<swap_integer_request_10_6>,
+ };
+
+ if (base::mac::IsOSSnowLeopard()) {
+ shim.look_up2_get_request_name =
+ &LaunchdLookUp2GetRequestName<look_up2_request_10_6>;
+ } else if (base::mac::IsOSLionOrLater() &&
+ !base::mac::IsOSYosemiteOrLater()) {
+ shim.look_up2_get_request_name =
+ &LaunchdLookUp2GetRequestName<look_up2_request_10_7>;
+ } else {
+ DLOG(ERROR) << "Unknown OS, using launchd compatibility shim from 10.7.";
+ shim.look_up2_get_request_name =
+ &LaunchdLookUp2GetRequestName<look_up2_request_10_7>;
+ }
+
+ return shim;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/os_compatibility.h b/chromium/sandbox/mac/os_compatibility.h
new file mode 100644
index 00000000000..aaedcb8c812
--- /dev/null
+++ b/chromium/sandbox/mac/os_compatibility.h
@@ -0,0 +1,52 @@
+// 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.
+
+// This file is used to handle differences in Mach message IDs and structures
+// that occur between different OS versions. The Mach messages that the sandbox
+// is interested in are decoded using information derived from the open-source
+// libraries, i.e. <http://www.opensource.apple.com/source/launchd/>. While
+// these messages definitions are open source, they are not considered part of
+// the stable OS API, and so differences do exist between OS versions.
+
+#ifndef SANDBOX_MAC_OS_COMPATIBILITY_H_
+#define SANDBOX_MAC_OS_COMPATIBILITY_H_
+
+#include <mach/mach.h>
+
+#include <string>
+
+namespace sandbox {
+
+typedef std::string (*LookUp2GetRequestName)(const mach_msg_header_t*);
+typedef void (*LookUp2FillReply)(mach_msg_header_t*, mach_port_t service_port);
+
+typedef bool (*SwapIntegerIsGetOnly)(const mach_msg_header_t*);
+
+struct LaunchdCompatibilityShim {
+ // The msgh_id for look_up2.
+ mach_msg_id_t msg_id_look_up2;
+
+ // The msgh_id for swap_integer.
+ mach_msg_id_t msg_id_swap_integer;
+
+ // A function to take a look_up2 message and return the string service name
+ // that was requested via the message.
+ LookUp2GetRequestName look_up2_get_request_name;
+
+ // A function to formulate a reply to a look_up2 message, given the reply
+ // message and the port to return as the service.
+ LookUp2FillReply look_up2_fill_reply;
+
+ // A function to take a swap_integer message and return true if the message
+ // is only getting the value of a key, neither setting it directly, nor
+ // swapping two keys.
+ SwapIntegerIsGetOnly swap_integer_is_get_only;
+};
+
+// Gets the compatibility shim for the launchd job subsystem.
+const LaunchdCompatibilityShim GetLaunchdCompatibilityShim();
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_OS_COMPATIBILITY_H_
diff --git a/chromium/sandbox/mac/policy.cc b/chromium/sandbox/mac/policy.cc
new file mode 100644
index 00000000000..293255adefc
--- /dev/null
+++ b/chromium/sandbox/mac/policy.cc
@@ -0,0 +1,56 @@
+// 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/mac/policy.h"
+
+namespace sandbox {
+
+Rule::Rule()
+ : result(POLICY_DECISION_INVALID),
+ substitute_port(MACH_PORT_NULL) {
+}
+
+Rule::Rule(PolicyDecision result)
+ : result(result),
+ substitute_port(MACH_PORT_NULL) {
+}
+
+Rule::Rule(mach_port_t override_port)
+ : result(POLICY_SUBSTITUTE_PORT),
+ substitute_port(override_port) {
+}
+
+BootstrapSandboxPolicy::BootstrapSandboxPolicy()
+ : default_rule(POLICY_DENY_ERROR) {
+}
+
+BootstrapSandboxPolicy::~BootstrapSandboxPolicy() {}
+
+static bool IsRuleValid(const Rule& rule) {
+ if (!(rule.result > POLICY_DECISION_INVALID &&
+ rule.result < POLICY_DECISION_LAST)) {
+ return false;
+ }
+ if (rule.result == POLICY_SUBSTITUTE_PORT) {
+ if (rule.substitute_port == MACH_PORT_NULL)
+ return false;
+ } else {
+ if (rule.substitute_port != MACH_PORT_NULL)
+ return false;
+ }
+ return true;
+}
+
+bool IsPolicyValid(const BootstrapSandboxPolicy& policy) {
+ if (!IsRuleValid(policy.default_rule))
+ return false;
+
+ for (const auto& pair : policy.rules) {
+ if (!IsRuleValid(pair.second))
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/policy.h b/chromium/sandbox/mac/policy.h
new file mode 100644
index 00000000000..e500468237a
--- /dev/null
+++ b/chromium/sandbox/mac/policy.h
@@ -0,0 +1,70 @@
+// 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_MAC_POLICY_H_
+#define SANDBOX_MAC_POLICY_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <string>
+
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+enum PolicyDecision {
+ POLICY_DECISION_INVALID,
+ // Explicitly allows the real service to be looked up from launchd.
+ POLICY_ALLOW,
+ // Deny the look up request by replying with a MIG error. This is the
+ // default behavior for servers not given an explicit rule.
+ POLICY_DENY_ERROR,
+ // Deny the look up request with a well-formed reply containing a
+ // Mach port with a send right, messages to which will be ignored.
+ POLICY_DENY_DUMMY_PORT,
+ // Reply to the look up request with a send right to the substitute_port
+ // specified in the Rule.
+ POLICY_SUBSTITUTE_PORT,
+ POLICY_DECISION_LAST,
+};
+
+// A Rule expresses the action to take when a service port is requested via
+// bootstrap_look_up. If |result| is not POLICY_SUBSTITUTE_PORT, then
+// |substitute_port| must be NULL. If result is POLICY_SUBSTITUTE_PORT, then
+// |substitute_port| must not be NULL.
+struct SANDBOX_EXPORT Rule {
+ Rule();
+ explicit Rule(PolicyDecision result);
+ explicit Rule(mach_port_t override_port);
+
+ PolicyDecision result;
+
+ // The Rule does not take ownership of this port, but additional send rights
+ // will be allocated to it before it is sent to a client. This name must
+ // denote a send right that can duplicated with MACH_MSG_TYPE_COPY_SEND.
+ mach_port_t substitute_port;
+};
+
+// A policy object manages the rules enforced on a target sandboxed process.
+struct SANDBOX_EXPORT BootstrapSandboxPolicy {
+ typedef std::map<std::string, Rule> NamedRules;
+
+ BootstrapSandboxPolicy();
+ ~BootstrapSandboxPolicy();
+
+ // The default action to take if the server name being looked up is not
+ // present in |rules|.
+ Rule default_rule;
+
+ // A map of bootstrap server names to policy Rules.
+ NamedRules rules;
+};
+
+// Checks that a policy is well-formed.
+SANDBOX_EXPORT bool IsPolicyValid(const BootstrapSandboxPolicy& policy);
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_POLICY_H_
diff --git a/chromium/sandbox/mac/policy_unittest.cc b/chromium/sandbox/mac/policy_unittest.cc
new file mode 100644
index 00000000000..54e0e748951
--- /dev/null
+++ b/chromium/sandbox/mac/policy_unittest.cc
@@ -0,0 +1,98 @@
+// 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/mac/policy.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(PolicyTest, ValidEmptyPolicy) {
+ EXPECT_TRUE(IsPolicyValid(BootstrapSandboxPolicy()));
+}
+
+TEST(PolicyTest, ValidPolicy) {
+ BootstrapSandboxPolicy policy;
+ policy.rules["allow"] = Rule(POLICY_ALLOW);
+ policy.rules["deny_error"] = Rule(POLICY_DENY_ERROR);
+ policy.rules["deny_dummy"] = Rule(POLICY_DENY_DUMMY_PORT);
+ policy.rules["substitue"] = Rule(mach_task_self());
+ EXPECT_TRUE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyEmptyRule) {
+ Rule rule;
+ BootstrapSandboxPolicy policy;
+ policy.rules["test"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicySubstitue) {
+ Rule rule(POLICY_SUBSTITUTE_PORT);
+ BootstrapSandboxPolicy policy;
+ policy.rules["test"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyWithPortAllow) {
+ Rule rule(POLICY_ALLOW);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.rules["allow"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyWithPortDenyError) {
+ Rule rule(POLICY_DENY_ERROR);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.rules["deny_error"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyWithPortDummy) {
+ Rule rule(POLICY_DENY_DUMMY_PORT);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.rules["deny_dummy"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRule) {
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = Rule();
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleSubstitue) {
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = Rule(POLICY_SUBSTITUTE_PORT);
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleWithPortAllow) {
+ Rule rule(POLICY_ALLOW);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleWithPortDenyError) {
+ Rule rule(POLICY_DENY_ERROR);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleWithPortDummy) {
+ Rule rule(POLICY_DENY_DUMMY_PORT);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/mac/sandbox_mac.gypi b/chromium/sandbox/mac/sandbox_mac.gypi
new file mode 100644
index 00000000000..36d6901667b
--- /dev/null
+++ b/chromium/sandbox/mac/sandbox_mac.gypi
@@ -0,0 +1,100 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': '<(component)',
+ 'sources': [
+ 'bootstrap_sandbox.cc',
+ 'bootstrap_sandbox.h',
+ 'launchd_interception_server.cc',
+ 'launchd_interception_server.h',
+ 'mach_message_server.cc',
+ 'mach_message_server.h',
+ 'os_compatibility.cc',
+ 'os_compatibility.h',
+ 'policy.cc',
+ 'policy.h',
+ 'xpc.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
+ ],
+ },
+ 'actions': [
+ {
+ 'variables': {
+ 'generate_stubs_script': '../tools/generate_stubs/generate_stubs.py',
+ 'generate_stubs_header_path': 'xpc_stubs_header.fragment',
+ 'generate_stubs_sig_public_path': 'xpc_stubs.sig',
+ 'generate_stubs_sig_private_path': 'xpc_private_stubs.sig',
+ 'generate_stubs_project': 'sandbox/mac',
+ 'generate_stubs_output_stem': 'xpc_stubs',
+ },
+ 'action_name': 'generate_stubs',
+ 'inputs': [
+ '<(generate_stubs_script)',
+ '<(generate_stubs_header_path)',
+ '<(generate_stubs_sig_public_path)',
+ '<(generate_stubs_sig_private_path)',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/<(generate_stubs_output_stem).cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(generate_stubs_project)/<(generate_stubs_output_stem).h',
+ ],
+ 'action': [
+ 'python',
+ '<(generate_stubs_script)',
+ '-i', '<(INTERMEDIATE_DIR)',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)/<(generate_stubs_project)',
+ '-t', 'posix_stubs',
+ '-e', '<(generate_stubs_header_path)',
+ '-s', '<(generate_stubs_output_stem)',
+ '-p', '<(generate_stubs_project)',
+ '<(generate_stubs_sig_public_path)',
+ '<(generate_stubs_sig_private_path)',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'message': 'Generating XPC stubs for 10.6 compatability.',
+ },
+ ],
+ },
+ {
+ 'target_name': 'sandbox_mac_unittests',
+ 'type': 'executable',
+ 'sources': [
+ 'bootstrap_sandbox_unittest.mm',
+ 'policy_unittest.cc',
+ ],
+ 'dependencies': [
+ 'sandbox',
+ '../base/base.gyp:base',
+ '../base/base.gyp:run_all_unittests',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ },
+ ],
+}
diff --git a/chromium/sandbox/mac/xpc.h b/chromium/sandbox/mac/xpc.h
new file mode 100644
index 00000000000..1cbe9ca7d40
--- /dev/null
+++ b/chromium/sandbox/mac/xpc.h
@@ -0,0 +1,40 @@
+// 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.
+
+// This file provides forward declarations for XPC symbols that are not
+// present in the 10.6 SDK. It uses generate_stubs to produce code to
+// dynamically load the libxpc.dylib library and set up a stub table, with
+// the same names as the real XPC functions.
+
+#ifndef SANDBOX_MAC_XPC_H_
+#define SANDBOX_MAC_XPC_H_
+
+#include <mach/mach.h>
+
+// C++ library loader.
+#include "sandbox/mac/xpc_stubs.h"
+
+// Declares XPC object types. This includes <xpc/xpc.h> if available.
+#include "sandbox/mac/xpc_stubs_header.fragment"
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+extern "C" {
+// Signatures for XPC public functions that are loaded by xpc_stubs.h.
+#include "sandbox/mac/xpc_stubs.sig"
+// Signatures for private XPC functions.
+#include "sandbox/mac/xpc_private_stubs.sig"
+} // extern "C"
+
+#else
+
+// Signatures for private XPC functions.
+extern "C" {
+#include "sandbox/mac/xpc_private_stubs.sig"
+} // extern "C"
+
+#endif
+
+#endif // SANDBOX_MAC_XPC_H_
diff --git a/chromium/sandbox/mac/xpc_private_stubs.sig b/chromium/sandbox/mac/xpc_private_stubs.sig
new file mode 100644
index 00000000000..33db194ebdf
--- /dev/null
+++ b/chromium/sandbox/mac/xpc_private_stubs.sig
@@ -0,0 +1,17 @@
+// 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.
+
+// This file contains declarations of private XPC functions. This file is
+// used for both forward declarations of private symbols and to use with
+// tools/generate_stubs for creating a dynamic library loader.
+
+// Dictionary manipulation.
+void xpc_dictionary_set_mach_send(xpc_object_t dict, const char* name, mach_port_t port);
+
+// Pipe methods.
+xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, int flags);
+int xpc_pipe_receive(mach_port_t port, xpc_object_t* message);
+int xpc_pipe_routine(xpc_pipe_t pipe, xpc_object_t request, xpc_object_t* reply);
+int xpc_pipe_routine_reply(xpc_object_t reply);
+int xpc_pipe_routine_forward(xpc_pipe_t forward_to, xpc_object_t request);
diff --git a/chromium/sandbox/mac/xpc_stubs.sig b/chromium/sandbox/mac/xpc_stubs.sig
new file mode 100644
index 00000000000..5020ffd5360
--- /dev/null
+++ b/chromium/sandbox/mac/xpc_stubs.sig
@@ -0,0 +1,16 @@
+// 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.
+
+// This file contains declarations of public XPC functions used in the sandbox.
+// This file is used with tools/generate_stubs for creating a dynamic library
+// loader.
+
+// XPC object management.
+void xpc_release(xpc_object_t object);
+
+// Dictionary manipulation.
+const char* xpc_dictionary_get_string(xpc_object_t dictionary, const char* key);
+uint64_t xpc_dictionary_get_uint64(xpc_object_t dictionary, const char* key);
+void xpc_dictionary_set_int64(xpc_object_t dictionary, const char* key, int64_t value);
+xpc_object_t xpc_dictionary_create_reply(xpc_object_t request);
diff --git a/chromium/sandbox/mac/xpc_stubs_header.fragment b/chromium/sandbox/mac/xpc_stubs_header.fragment
new file mode 100644
index 00000000000..a29907ef3fd
--- /dev/null
+++ b/chromium/sandbox/mac/xpc_stubs_header.fragment
@@ -0,0 +1,27 @@
+// 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_MAC_XPC_STUBS_HEADER_FRAGMENT_
+#define SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
+
+// Declare or include public types.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+extern "C" {
+typedef void* xpc_object_t;
+} // extern "C"
+
+#else
+
+#include <xpc/xpc.h>
+
+#endif
+
+// Declare private types.
+extern "C" {
+typedef struct _xpc_pipe_s* xpc_pipe_t;
+} // extern "C"
+
+#endif // SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
diff --git a/chromium/sandbox/sandbox.gyp b/chromium/sandbox/sandbox.gyp
index b48727a28ba..f93fa1862a7 100644
--- a/chromium/sandbox/sandbox.gyp
+++ b/chromium/sandbox/sandbox.gyp
@@ -17,6 +17,11 @@
'linux/sandbox_linux.gypi',
],
}],
+ [ 'OS=="mac" and OS!="ios"', {
+ 'includes': [
+ 'mac/sandbox_mac.gypi',
+ ],
+ }],
[ 'OS!="win" and OS!="mac" and OS!="linux" and OS!="android"', {
# A 'default' to accomodate the "sandbox" target.
'targets': [
diff --git a/chromium/sandbox/sandbox_export.h b/chromium/sandbox/sandbox_export.h
new file mode 100644
index 00000000000..40a40366406
--- /dev/null
+++ b/chromium/sandbox/sandbox_export.h
@@ -0,0 +1,29 @@
+// 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_SANDBOX_EXPORT_H_
+#define SANDBOX_SANDBOX_EXPORT_H_
+
+#if defined(WIN32)
+#error "sandbox_export.h does not support WIN32."
+#endif
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(SANDBOX_IMPLEMENTATION)
+#define SANDBOX_EXPORT __attribute__((visibility("default")))
+#define SANDBOX_EXPORT_PRIVATE __attribute__((visibility("default")))
+#else
+#define SANDBOX_EXPORT
+#define SANDBOX_EXPORT_PRIVATE
+#endif // defined(SANDBOX_IMPLEMENTATION)
+
+#else // defined(COMPONENT_BUILD)
+
+#define SANDBOX_EXPORT
+#define SANDBOX_EXPORT_PRIVATE
+
+#endif // defined(COMPONENT_BUILD)
+
+#endif // SANDBOX_SANDBOX_EXPORT_H_
diff --git a/chromium/sandbox/sandbox_linux_unittests.isolate b/chromium/sandbox/sandbox_linux_unittests.isolate
new file mode 100644
index 00000000000..89865668ba1
--- /dev/null
+++ b/chromium/sandbox/sandbox_linux_unittests.isolate
@@ -0,0 +1,27 @@
+# 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.
+
+# Because of a limitation in isolate_driver.py, this file needs to be in
+# the same directory as the main .gyp file.
+
+{
+ 'conditions': [
+ ['OS=="android" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '<(PRODUCT_DIR)/sandbox_linux_unittests',
+ ],
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/sandbox_linux_unittests',
+ ],
+ 'read_only': 1,
+ },
+ }],
+ ],
+ 'includes': [
+ # This is needed because of base/ dependencies on
+ # icudtl.dat.
+ '../base/base.isolate',
+ ],
+}
diff --git a/chromium/sandbox/win/BUILD.gn b/chromium/sandbox/win/BUILD.gn
new file mode 100644
index 00000000000..3c5bca61001
--- /dev/null
+++ b/chromium/sandbox/win/BUILD.gn
@@ -0,0 +1,285 @@
+# 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.
+
+source_set("sandbox") {
+ sources = [
+ "src/acl.cc",
+ "src/acl.h",
+ "src/app_container.cc",
+ "src/app_container.h",
+ "src/broker_services.cc",
+ "src/broker_services.h",
+ "src/crosscall_client.h",
+ "src/crosscall_params.h",
+ "src/crosscall_server.cc",
+ "src/crosscall_server.h",
+ "src/eat_resolver.cc",
+ "src/eat_resolver.h",
+ "src/filesystem_dispatcher.cc",
+ "src/filesystem_dispatcher.h",
+ "src/filesystem_interception.cc",
+ "src/filesystem_interception.h",
+ "src/filesystem_policy.cc",
+ "src/filesystem_policy.h",
+ "src/handle_closer.cc",
+ "src/handle_closer.h",
+ "src/handle_closer_agent.cc",
+ "src/handle_closer_agent.h",
+ "src/handle_dispatcher.cc",
+ "src/handle_dispatcher.h",
+ "src/handle_interception.cc",
+ "src/handle_interception.h",
+ "src/handle_policy.cc",
+ "src/handle_policy.h",
+ "src/handle_table.cc",
+ "src/handle_table.h",
+ "src/interception.cc",
+ "src/interception.h",
+ "src/interception_agent.cc",
+ "src/interception_agent.h",
+ "src/interception_internal.h",
+ "src/interceptors.h",
+ "src/internal_types.h",
+ "src/ipc_tags.h",
+ "src/job.cc",
+ "src/job.h",
+ "src/named_pipe_dispatcher.cc",
+ "src/named_pipe_dispatcher.h",
+ "src/named_pipe_interception.cc",
+ "src/named_pipe_interception.h",
+ "src/named_pipe_policy.cc",
+ "src/named_pipe_policy.h",
+ "src/nt_internals.h",
+ "src/policy_broker.cc",
+ "src/policy_broker.h",
+ "src/policy_engine_opcodes.cc",
+ "src/policy_engine_opcodes.h",
+ "src/policy_engine_params.h",
+ "src/policy_engine_processor.cc",
+ "src/policy_engine_processor.h",
+ "src/policy_low_level.cc",
+ "src/policy_low_level.h",
+ "src/policy_params.h",
+ "src/policy_target.cc",
+ "src/policy_target.h",
+ "src/process_mitigations.cc",
+ "src/process_mitigations.h",
+ "src/process_thread_dispatcher.cc",
+ "src/process_thread_dispatcher.h",
+ "src/process_thread_interception.cc",
+ "src/process_thread_interception.h",
+ "src/process_thread_policy.cc",
+ "src/process_thread_policy.h",
+ "src/registry_dispatcher.cc",
+ "src/registry_dispatcher.h",
+ "src/registry_interception.cc",
+ "src/registry_interception.h",
+ "src/registry_policy.cc",
+ "src/registry_policy.h",
+ "src/resolver.cc",
+ "src/resolver.h",
+ "src/restricted_token_utils.cc",
+ "src/restricted_token_utils.h",
+ "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",
+ "src/sandbox_policy_base.cc",
+ "src/sandbox_policy_base.h",
+ "src/sandbox_policy.h",
+ "src/sandbox_types.h",
+ "src/sandbox_utils.cc",
+ "src/sandbox_utils.h",
+ "src/sandbox.cc",
+ "src/sandbox.h",
+ "src/security_level.h",
+ "src/service_resolver.cc",
+ "src/service_resolver.h",
+ "src/shared_handles.cc",
+ "src/shared_handles.h",
+ "src/sharedmem_ipc_client.cc",
+ "src/sharedmem_ipc_client.h",
+ "src/sharedmem_ipc_server.cc",
+ "src/sharedmem_ipc_server.h",
+ "src/sid.cc",
+ "src/sid.h",
+ "src/sync_dispatcher.cc",
+ "src/sync_dispatcher.h",
+ "src/sync_interception.cc",
+ "src/sync_interception.h",
+ "src/sync_policy.cc",
+ "src/sync_policy.h",
+ "src/target_interceptions.cc",
+ "src/target_interceptions.h",
+ "src/target_process.cc",
+ "src/target_process.h",
+ "src/target_services.cc",
+ "src/target_services.h",
+ "src/win_utils.cc",
+ "src/win_utils.h",
+ "src/win2k_threadpool.cc",
+ "src/win2k_threadpool.h",
+ "src/window.cc",
+ "src/window.h",
+ ]
+
+ if (cpu_arch == "x64" ){
+ sources += [
+ "src/interceptors_64.cc",
+ "src/interceptors_64.h",
+ "src/resolver_64.cc",
+ "src/service_resolver_64.cc",
+ "src/Wow64_64.cc",
+ ]
+ } else if (cpu_arch == "x86") {
+ sources += [
+ "src/resolver_32.cc",
+ "src/service_resolver_32.cc",
+ "src/sidestep_resolver.cc",
+ "src/sidestep_resolver.h",
+ "src/sidestep/ia32_modrm_map.cpp",
+ "src/sidestep/ia32_opcode_map.cpp",
+ "src/sidestep/mini_disassembler_types.h",
+ "src/sidestep/mini_disassembler.cpp",
+ "src/sidestep/mini_disassembler.h",
+ "src/sidestep/preamble_patcher_with_stub.cpp",
+ "src/sidestep/preamble_patcher.h",
+ "src/Wow64.cc",
+ "src/Wow64.h",
+ ]
+ }
+
+ deps = [
+ ":copy_wow_helper",
+ "//base",
+ ]
+}
+
+if (cpu_arch == "x86") {
+ # Make a target that copies the wow_helper files to the out dir.
+ #
+ # TODO(brettw) we can probably just build this now that we have proper
+ # toolchain support.
+ copy("copy_wow_helper") {
+ sources = [
+ "wow_helper/wow_helper.exe",
+ "wow_helper/wow_helper.pdb",
+ ]
+ outputs = [ "$root_out_dir/{{source_file_part}}" ]
+ }
+}
+
+test("sbox_integration_tests") {
+ sources = [
+ "src/app_container_test.cc",
+ "src/file_policy_test.cc",
+ "src/handle_inheritance_test.cc",
+ "src/handle_policy_test.cc",
+ "tests/integration_tests/integration_tests_test.cc",
+ "src/handle_closer_test.cc",
+ "src/integrity_level_test.cc",
+ "src/ipc_ping_test.cc",
+ "src/named_pipe_policy_test.cc",
+ "src/policy_target_test.cc",
+ "src/process_mitigations_test.cc",
+ "src/process_policy_test.cc",
+ "src/registry_policy_test.cc",
+ "src/sync_policy_test.cc",
+ "src/sync_policy_test.h",
+ "src/unload_dll_test.cc",
+ "tests/common/controller.cc",
+ "tests/common/controller.h",
+ "tests/common/test_utils.cc",
+ "tests/common/test_utils.h",
+ "tests/integration_tests/integration_tests.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//testing/gtest",
+ ]
+}
+
+test("sbox_validation_tests") {
+ sources = [
+ "tests/common/controller.cc",
+ "tests/common/controller.h",
+ "tests/validation_tests/unit_tests.cc",
+ "tests/validation_tests/commands.cc",
+ "tests/validation_tests/commands.h",
+ "tests/validation_tests/suite.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//testing/gtest",
+ ]
+}
+
+test("sbox_unittests") {
+ sources = [
+ "src/app_container_unittest.cc",
+ "src/interception_unittest.cc",
+ "src/service_resolver_unittest.cc",
+ "src/restricted_token_unittest.cc",
+ "src/job_unittest.cc",
+ "src/sid_unittest.cc",
+ "src/policy_engine_unittest.cc",
+ "src/policy_low_level_unittest.cc",
+ "src/policy_opcodes_unittest.cc",
+ "src/ipc_unittest.cc",
+ "src/threadpool_unittest.cc",
+ "src/win_utils_unittest.cc",
+ "tests/common/test_utils.cc",
+ "tests/common/test_utils.h",
+ "tests/unit_tests/unit_tests.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//testing/gtest",
+ ]
+}
+
+test("sandbox_poc") {
+ sources = [
+ "sandbox_poc/main_ui_window.cc",
+ "sandbox_poc/main_ui_window.h",
+ "sandbox_poc/resource.h",
+ "sandbox_poc/sandbox.cc",
+ "sandbox_poc/sandbox.h",
+ "sandbox_poc/sandbox.ico",
+ "sandbox_poc/sandbox.rc",
+ ]
+
+ configs -= [ "//build/config/win:console" ]
+ configs += [ "//build/config/win:windowed" ]
+
+ libs = [ "comctl32.lib" ]
+
+ deps = [
+ ":sandbox",
+ ":pocdll",
+ ]
+}
+
+shared_library("pocdll") {
+ sources = [
+ "sandbox_poc/pocdll/exports.h",
+ "sandbox_poc/pocdll/fs.cc",
+ "sandbox_poc/pocdll/handles.cc",
+ "sandbox_poc/pocdll/invasive.cc",
+ "sandbox_poc/pocdll/network.cc",
+ "sandbox_poc/pocdll/pocdll.cc",
+ "sandbox_poc/pocdll/processes_and_threads.cc",
+ "sandbox_poc/pocdll/registry.cc",
+ "sandbox_poc/pocdll/spyware.cc",
+ "sandbox_poc/pocdll/utils.h",
+ ]
+
+ defines = [ "POCDLL_EXPORTS" ]
+}
diff --git a/chromium/sandbox/win/OWNERS b/chromium/sandbox/win/OWNERS
new file mode 100644
index 00000000000..9ccaeb39f55
--- /dev/null
+++ b/chromium/sandbox/win/OWNERS
@@ -0,0 +1,3 @@
+cpu@chromium.org
+jschuh@chromium.org
+rvargas@chromium.org
diff --git a/chromium/sandbox/win/sandbox_win.gypi b/chromium/sandbox/win/sandbox_win.gypi
index 1fa82794bbf..7d9cf949fa7 100644
--- a/chromium/sandbox/win/sandbox_win.gypi
+++ b/chromium/sandbox/win/sandbox_win.gypi
@@ -74,6 +74,12 @@
'src/policy_target.h',
'src/process_mitigations.cc',
'src/process_mitigations.h',
+ 'src/process_mitigations_win32k_dispatcher.cc',
+ 'src/process_mitigations_win32k_dispatcher.h',
+ 'src/process_mitigations_win32k_interception.cc',
+ 'src/process_mitigations_win32k_interception.h',
+ 'src/process_mitigations_win32k_policy.cc',
+ 'src/process_mitigations_win32k_policy.h',
'src/process_thread_dispatcher.cc',
'src/process_thread_dispatcher.h',
'src/process_thread_interception.cc',
@@ -174,7 +180,6 @@
'sandbox_windows_target': 1,
},
'dependencies': [
- '../testing/gtest.gyp:gtest',
'../base/base.gyp:base',
'../base/base.gyp:base_static',
],
@@ -209,6 +214,7 @@
'type': 'executable',
'dependencies': [
'sandbox',
+ '../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
],
'sources': [
@@ -240,6 +246,7 @@
'type': 'executable',
'dependencies': [
'sandbox',
+ '../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
],
'sources': [
@@ -256,6 +263,7 @@
'type': 'executable',
'dependencies': [
'sandbox',
+ '../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
],
'sources': [
@@ -337,7 +345,7 @@
'target_arch': 'x64',
},
'dependencies': [
- '../base/base.gyp:base_nacl_win64',
+ '../base/base.gyp:base_win64',
'../base/base.gyp:base_static_win64',
],
'configurations': {
diff --git a/chromium/sandbox/win/src/Wow64.cc b/chromium/sandbox/win/src/Wow64.cc
index b11026b1a52..59df1d6f674 100644
--- a/chromium/sandbox/win/src/Wow64.cc
+++ b/chromium/sandbox/win/src/Wow64.cc
@@ -153,7 +153,8 @@ bool Wow64::RunWowHelper(void* buffer) {
L"wow_helper.exe\" " << child_->ProcessId() << " " <<
bit_cast<ULONG>(buffer);
- scoped_ptr_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str()));
+ scoped_ptr<wchar_t, base::FreeDeleter>
+ writable_command(_wcsdup(command.str().c_str()));
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
diff --git a/chromium/sandbox/win/src/acl.cc b/chromium/sandbox/win/src/acl.cc
index bf59d962a54..f140c7e6c4f 100644
--- a/chromium/sandbox/win/src/acl.cc
+++ b/chromium/sandbox/win/src/acl.cc
@@ -11,8 +11,9 @@
namespace sandbox {
-bool GetDefaultDacl(HANDLE token,
- scoped_ptr_malloc<TOKEN_DEFAULT_DACL>* default_dacl) {
+bool GetDefaultDacl(
+ HANDLE token,
+ scoped_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_dacl) {
if (token == NULL)
return false;
@@ -59,7 +60,7 @@ bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) {
if (token == NULL)
return false;
- scoped_ptr_malloc<TOKEN_DEFAULT_DACL> default_dacl;
+ scoped_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter> default_dacl;
if (!GetDefaultDacl(token, &default_dacl))
return false;
@@ -81,7 +82,7 @@ bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) {
DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(malloc(size));
- scoped_ptr_malloc<TOKEN_USER> token_user_ptr(token_user);
+ scoped_ptr<TOKEN_USER, base::FreeDeleter> token_user_ptr(token_user);
if (!::GetTokenInformation(token, TokenUser, token_user, size, &size))
return false;
diff --git a/chromium/sandbox/win/src/acl.h b/chromium/sandbox/win/src/acl.h
index 531259fbebd..b5021e7be86 100644
--- a/chromium/sandbox/win/src/acl.h
+++ b/chromium/sandbox/win/src/acl.h
@@ -14,8 +14,9 @@
namespace sandbox {
// Returns the default dacl from the token passed in.
-bool GetDefaultDacl(HANDLE token,
- scoped_ptr_malloc<TOKEN_DEFAULT_DACL>* default_dacl);
+bool GetDefaultDacl(
+ HANDLE token,
+ scoped_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_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
diff --git a/chromium/sandbox/win/src/app_container.cc b/chromium/sandbox/win/src/app_container.cc
index 826b5614f71..f8d75419159 100644
--- a/chromium/sandbox/win/src/app_container.cc
+++ b/chromium/sandbox/win/src/app_container.cc
@@ -16,7 +16,7 @@ namespace {
// Converts the passed in sid string to a PSID that must be relased with
// LocalFree.
-PSID ConvertSid(const string16& sid) {
+PSID ConvertSid(const base::string16& sid) {
PSID local_sid;
if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
return NULL;
@@ -49,8 +49,8 @@ AppContainerAttributes::~AppContainerAttributes() {
}
ResultCode AppContainerAttributes::SetAppContainer(
- const string16& app_container_sid,
- const std::vector<string16>& capabilities) {
+ const base::string16& app_container_sid,
+ const std::vector<base::string16>& capabilities) {
DCHECK(!capabilities_.AppContainerSid);
DCHECK(attributes_.empty());
capabilities_.AppContainerSid = ConvertSid(app_container_sid);
@@ -94,7 +94,8 @@ bool AppContainerAttributes::HasAppContainer() const {
return (capabilities_.AppContainerSid != NULL);
}
-ResultCode CreateAppContainer(const string16& sid, const string16& name) {
+ResultCode CreateAppContainer(const base::string16& sid,
+ const base::string16& name) {
PSID local_sid;
if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
return SBOX_ERROR_INVALID_APP_CONTAINER;
@@ -121,7 +122,7 @@ ResultCode CreateAppContainer(const string16& sid, const string16& name) {
return operation_result;
}
-ResultCode DeleteAppContainer(const string16& sid) {
+ResultCode DeleteAppContainer(const base::string16& sid) {
PSID local_sid;
if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
return SBOX_ERROR_INVALID_APP_CONTAINER;
@@ -146,10 +147,10 @@ ResultCode DeleteAppContainer(const string16& sid) {
return operation_result;
}
-string16 LookupAppContainer(const string16& sid) {
+base::string16 LookupAppContainer(const base::string16& sid) {
PSID local_sid;
if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
- return string16();
+ return base::string16();
typedef HRESULT (WINAPI* AppContainerLookupMonikerPtr)(PSID sid,
LPWSTR* moniker);
@@ -166,14 +167,14 @@ string16 LookupAppContainer(const string16& sid) {
}
if (!AppContainerLookupMoniker || !AppContainerFreeMemory)
- return string16();
+ return base::string16();
wchar_t* buffer = NULL;
HRESULT rv = AppContainerLookupMoniker(local_sid, &buffer);
if (FAILED(rv))
- return string16();
+ return base::string16();
- string16 name(buffer);
+ base::string16 name(buffer);
if (!AppContainerFreeMemory(buffer))
NOTREACHED();
return name;
diff --git a/chromium/sandbox/win/src/app_container.h b/chromium/sandbox/win/src/app_container.h
index 34b43e95f57..8125d706fb4 100644
--- a/chromium/sandbox/win/src/app_container.h
+++ b/chromium/sandbox/win/src/app_container.h
@@ -29,8 +29,8 @@ class AppContainerAttributes {
~AppContainerAttributes();
// Sets the AppContainer and capabilities to be used with the new process.
- ResultCode SetAppContainer(const string16& app_container_sid,
- const std::vector<string16>& capabilities);
+ ResultCode SetAppContainer(const base::string16& app_container_sid,
+ const std::vector<base::string16>& capabilities);
// Updates the proc_thred attribute list of the provided startup_information
// with the app container related data.
@@ -53,15 +53,16 @@ class AppContainerAttributes {
// AppContainer, and |name| will be used as both the display name and moniker.
// This function fails if the OS doesn't support AppContainers, or if there is
// an AppContainer registered with the same id.
-ResultCode CreateAppContainer(const string16& sid, const string16& name);
+ResultCode CreateAppContainer(const base::string16& sid,
+ const base::string16& name);
// Deletes an AppContainer previously created with a successfull call to
// CreateAppContainer.
-ResultCode DeleteAppContainer(const string16& sid);
+ResultCode DeleteAppContainer(const base::string16& sid);
// Retrieves the name associated with the provided AppContainer sid. Returns an
// empty string if the AppContainer is not registered with the system.
-string16 LookupAppContainer(const string16& sid);
+base::string16 LookupAppContainer(const base::string16& sid);
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/app_container_test.cc b/chromium/sandbox/win/src/app_container_test.cc
index 3b33ca5c901..1bfab2c3145 100644
--- a/chromium/sandbox/win/src/app_container_test.cc
+++ b/chromium/sandbox/win/src/app_container_test.cc
@@ -23,7 +23,8 @@ const wchar_t kAppContainerSid[] =
const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
-HANDLE CreateTaggedEvent(const string16& name, const string16& sid) {
+HANDLE CreateTaggedEvent(const base::string16& name,
+ const base::string16& sid) {
base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, name.c_str()));
if (!event.IsValid())
return NULL;
diff --git a/chromium/sandbox/win/src/app_container_unittest.cc b/chromium/sandbox/win/src/app_container_unittest.cc
index 936a9cbc7bf..4bce16a42b5 100644
--- a/chromium/sandbox/win/src/app_container_unittest.cc
+++ b/chromium/sandbox/win/src/app_container_unittest.cc
@@ -37,7 +37,7 @@ TEST(AppContainerTest, SecurityCapabilities) {
return;
scoped_ptr<AppContainerAttributes> attributes(new AppContainerAttributes);
- std::vector<string16> capabilities;
+ std::vector<base::string16> capabilities;
EXPECT_EQ(SBOX_ERROR_INVALID_APP_CONTAINER,
attributes->SetAppContainer(L"S-1-foo", capabilities));
diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc
index 921eb4f89e0..895d535a486 100644
--- a/chromium/sandbox/win/src/broker_services.cc
+++ b/chromium/sandbox/win/src/broker_services.cc
@@ -4,6 +4,8 @@
#include "sandbox/win/src/broker_services.h"
+#include <AclAPI.h>
+
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/platform_thread.h"
@@ -80,6 +82,29 @@ void DeregisterPeerTracker(PeerTracker* peer) {
}
}
+// Utility function to pack token values into a key for the cache map.
+uint32_t GenerateTokenCacheKey(const sandbox::PolicyBase* policy) {
+ const size_t kTokenShift = 3;
+ uint32_t key;
+
+ // Make sure our token values aren't too large to pack into the key.
+ static_assert(sandbox::USER_LAST <= (1 << kTokenShift),
+ "TokenLevel too large");
+ static_assert(sandbox::INTEGRITY_LEVEL_LAST <= (1 << kTokenShift),
+ "IntegrityLevel too large");
+ static_assert(sizeof(key) < (kTokenShift * 3),
+ "Token key type too small");
+
+ // The key is the enum values shifted to avoid overlap and OR'd together.
+ key = policy->GetInitialTokenLevel();
+ key <<= kTokenShift;
+ key |= policy->GetLockdownTokenLevel();
+ key <<= kTokenShift;
+ key |= policy->GetIntegrityLevel();
+
+ return key;
+}
+
} // namespace
namespace sandbox {
@@ -153,6 +178,13 @@ BrokerServicesBase::~BrokerServicesBase() {
// If job_port_ isn't NULL, assumes that the lock has been initialized.
if (job_port_)
::DeleteCriticalSection(&lock_);
+
+ // Close any token in the cache.
+ for (TokenCacheMap::iterator it = token_cache_.begin();
+ it != token_cache_.end(); ++it) {
+ ::CloseHandle(it->second.first);
+ ::CloseHandle(it->second.second);
+ }
}
TargetPolicy* BrokerServicesBase::CreatePolicy() {
@@ -246,6 +278,13 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
break;
}
+ case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
+ BOOL res = ::TerminateJobObject(tracker->job,
+ SBOX_FATAL_MEMORY_EXCEEDED);
+ DCHECK(res);
+ break;
+ }
+
default: {
NOTREACHED();
break;
@@ -299,10 +338,35 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
// with the soon to be created target process.
HANDLE initial_token_temp;
HANDLE lockdown_token_temp;
- ResultCode result = policy_base->MakeTokens(&initial_token_temp,
- &lockdown_token_temp);
- if (SBOX_ALL_OK != result)
- return result;
+ ResultCode result = SBOX_ALL_OK;
+
+ // Create the master tokens only once and save them in a cache. That way
+ // can just duplicate them to avoid hammering LSASS on every sandboxed
+ // process launch.
+ uint32_t token_key = GenerateTokenCacheKey(policy_base);
+ TokenCacheMap::iterator it = token_cache_.find(token_key);
+ if (it != token_cache_.end()) {
+ initial_token_temp = it->second.first;
+ lockdown_token_temp = it->second.second;
+ } else {
+ result = policy_base->MakeTokens(&initial_token_temp,
+ &lockdown_token_temp);
+ if (SBOX_ALL_OK != result)
+ return result;
+ token_cache_[token_key] =
+ std::pair<HANDLE, HANDLE>(initial_token_temp, lockdown_token_temp);
+ }
+
+ if (!::DuplicateToken(initial_token_temp, SecurityImpersonation,
+ &initial_token_temp)) {
+ return SBOX_ERROR_GENERIC;
+ }
+
+ if (!::DuplicateTokenEx(lockdown_token_temp, TOKEN_ALL_ACCESS, 0,
+ SecurityIdentification, TokenPrimary,
+ &lockdown_token_temp)) {
+ return SBOX_ERROR_GENERIC;
+ }
base::win::ScopedHandle initial_token(initial_token_temp);
base::win::ScopedHandle lockdown_token(lockdown_token_temp);
@@ -316,7 +380,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
// Initialize the startup information from the policy.
base::win::StartupInformation startup_info;
- string16 desktop = policy_base->GetAlternateDesktop();
+ base::string16 desktop = policy_base->GetAlternateDesktop();
if (!desktop.empty()) {
startup_info.startup_info()->lpDesktop =
const_cast<wchar_t*>(desktop.c_str());
@@ -486,7 +550,7 @@ ResultCode BrokerServicesBase::InstallAppContainer(const wchar_t* sid,
if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
return SBOX_ERROR_UNSUPPORTED;
- string16 old_name = LookupAppContainer(sid);
+ base::string16 old_name = LookupAppContainer(sid);
if (old_name.empty())
return CreateAppContainer(sid, name);
@@ -500,7 +564,7 @@ ResultCode BrokerServicesBase::UninstallAppContainer(const wchar_t* sid) {
if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
return SBOX_ERROR_UNSUPPORTED;
- string16 name = LookupAppContainer(sid);
+ base::string16 name = LookupAppContainer(sid);
if (name.empty())
return SBOX_ERROR_INVALID_APP_CONTAINER;
diff --git a/chromium/sandbox/win/src/broker_services.h b/chromium/sandbox/win/src/broker_services.h
index 11c10e0f884..ba189f41f83 100644
--- a/chromium/sandbox/win/src/broker_services.h
+++ b/chromium/sandbox/win/src/broker_services.h
@@ -8,6 +8,7 @@
#include <list>
#include <map>
#include <set>
+#include <utility>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/win/scoped_handle.h"
@@ -36,8 +37,8 @@ class PolicyBase;
// of the associated TargetProcess interface. In this implementation
// TargetProcess is a friend of BrokerServices where the later manages a
// collection of the former.
-class BrokerServicesBase : public BrokerServices,
- public SingletonBase<BrokerServicesBase> {
+class BrokerServicesBase FINAL : public BrokerServices,
+ public SingletonBase<BrokerServicesBase> {
public:
BrokerServicesBase();
@@ -105,6 +106,9 @@ class BrokerServicesBase : public BrokerServices,
// job. Consult |jobless_process_handles_| for handles of pocess without job.
std::set<DWORD> child_process_ids_;
+ typedef std::map<uint32_t, std::pair<HANDLE, HANDLE>> TokenCacheMap;
+ TokenCacheMap token_cache_;
+
DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase);
};
diff --git a/chromium/sandbox/win/src/crosscall_server.cc b/chromium/sandbox/win/src/crosscall_server.cc
index ab8b42113fa..65a908478f6 100644
--- a/chromium/sandbox/win/src/crosscall_server.cc
+++ b/chromium/sandbox/win/src/crosscall_server.cc
@@ -60,7 +60,6 @@ uint32 GetActualBufferSize(uint32 param_count, void* buffer_base) {
case 9:
return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize();
default:
- NOTREACHED();
return 0;
}
}
diff --git a/chromium/sandbox/win/src/handle_closer.cc b/chromium/sandbox/win/src/handle_closer.cc
index 39915a9b0a1..30e8977fa85 100644
--- a/chromium/sandbox/win/src/handle_closer.cc
+++ b/chromium/sandbox/win/src/handle_closer.cc
@@ -34,8 +34,8 @@ SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
HandleCloser::HandleCloser() {}
-ResultCode HandleCloser::AddHandle(const char16* handle_type,
- const char16* handle_name) {
+ResultCode HandleCloser::AddHandle(const base::char16* handle_type,
+ const base::char16* handle_name) {
if (!handle_type)
return SBOX_ERROR_BAD_PARAMS;
@@ -61,10 +61,10 @@ size_t HandleCloser::GetBufferSize() {
for (HandleMap::iterator i = handles_to_close_.begin();
i != handles_to_close_.end(); ++i) {
size_t bytes_entry = offsetof(HandleListEntry, handle_type) +
- (i->first.size() + 1) * sizeof(char16);
+ (i->first.size() + 1) * sizeof(base::char16);
for (HandleMap::mapped_type::iterator j = i->second.begin();
j != i->second.end(); ++j) {
- bytes_entry += ((*j).size() + 1) * sizeof(char16);
+ bytes_entry += ((*j).size() + 1) * sizeof(base::char16);
}
// Round up to the nearest multiple of word size.
@@ -119,8 +119,9 @@ bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
handle_info->record_bytes = buffer_bytes;
handle_info->num_handle_types = handles_to_close_.size();
- char16* output = reinterpret_cast<char16*>(&handle_info->handle_entries[0]);
- char16* end = reinterpret_cast<char16*>(
+ base::char16* output = reinterpret_cast<base::char16*>(
+ &handle_info->handle_entries[0]);
+ base::char16* end = reinterpret_cast<base::char16*>(
reinterpret_cast<char*>(buffer) + buffer_bytes);
for (HandleMap::iterator i = handles_to_close_.begin();
i != handles_to_close_.end(); ++i) {
@@ -153,28 +154,7 @@ bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
return output <= end;
}
-bool HandleCloser::SetupHandleInterceptions(InterceptionManager* manager) {
- // We need to intercept CreateThread if we're closing ALPC port clients.
- HandleMap::iterator names = handles_to_close_.find(L"ALPC Port");
- if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
- names != handles_to_close_.end() &&
- (names->second.empty() || names->second.size() == 0)) {
- if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread,
- CREATE_THREAD_ID, 28)) {
- return false;
- }
- if (!INTERCEPT_EAT(manager, kKerneldllName, GetUserDefaultLCID,
- GET_USER_DEFAULT_LCID_ID, 4)) {
- return false;
- }
-
- return true;
- }
-
- return true;
-}
-
-bool GetHandleName(HANDLE handle, string16* handle_name) {
+bool GetHandleName(HANDLE handle, base::string16* handle_name) {
static NtQueryObject QueryObject = NULL;
if (!QueryObject)
ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
diff --git a/chromium/sandbox/win/src/handle_closer.h b/chromium/sandbox/win/src/handle_closer.h
index a6f81d5b290..60473b3ea00 100644
--- a/chromium/sandbox/win/src/handle_closer.h
+++ b/chromium/sandbox/win/src/handle_closer.h
@@ -19,14 +19,15 @@ namespace sandbox {
// This is a map of handle-types to names that we need to close in the
// target process. A null set means we need to close all handles of the
// given type.
-typedef std::map<const string16, std::set<const string16> > HandleMap;
+typedef std::map<const base::string16, std::set<const base::string16> >
+ HandleMap;
// Type and set of corresponding handle names to close.
struct HandleListEntry {
size_t record_bytes; // Rounded to sizeof(size_t) bytes.
size_t offset_to_names; // Nul terminated strings of name_count names.
size_t name_count;
- char16 handle_type[1];
+ base::char16 handle_type[1];
};
// Global parameters and a pointer to the list of entries.
@@ -46,14 +47,12 @@ class HandleCloser {
// Adds a handle that will be closed in the target process after lockdown.
// A NULL value for handle_name indicates all handles of the specified type.
// An empty string for handle_name indicates the handle is unnamed.
- ResultCode AddHandle(const char16* handle_type, const char16* handle_name);
+ ResultCode AddHandle(const base::char16* handle_type,
+ const base::char16* handle_name);
// Serializes and copies the closer table into the target process.
bool InitializeTargetHandles(TargetProcess* target);
- // Adds any interceptions that may be required due to closed system handles.
- bool SetupHandleInterceptions(InterceptionManager* manager);
-
private:
// Calculates the memory needed to copy the serialized handles list (rounded
// to the nearest machine-word size).
@@ -68,7 +67,7 @@ class HandleCloser {
};
// Returns the object manager's name associated with a handle
-bool GetHandleName(HANDLE handle, string16* handle_name);
+bool GetHandleName(HANDLE handle, base::string16* handle_name);
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/handle_closer_agent.cc b/chromium/sandbox/win/src/handle_closer_agent.cc
index bc75e7322b2..07c6a09854d 100644
--- a/chromium/sandbox/win/src/handle_closer_agent.cc
+++ b/chromium/sandbox/win/src/handle_closer_agent.cc
@@ -49,9 +49,9 @@ void HandleCloserAgent::InitializeHandlesToClose() {
HandleListEntry* entry = g_handles_to_close->handle_entries;
for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) {
// Set the type name.
- char16* input = entry->handle_type;
+ base::char16* input = entry->handle_type;
HandleMap::mapped_type& handle_names = handles_to_close_[input];
- input = reinterpret_cast<char16*>(reinterpret_cast<char*>(entry)
+ input = reinterpret_cast<base::char16*>(reinterpret_cast<char*>(entry)
+ entry->offset_to_names);
// Grab all the handle names.
for (size_t j = 0; j < entry->name_count; ++j) {
@@ -65,9 +65,9 @@ void HandleCloserAgent::InitializeHandlesToClose() {
entry = reinterpret_cast<HandleListEntry*>(reinterpret_cast<char*>(entry)
+ entry->record_bytes);
- DCHECK(reinterpret_cast<char16*>(entry) >= input);
- DCHECK(reinterpret_cast<char16*>(entry) - input <
- sizeof(size_t) / sizeof(char16));
+ DCHECK(reinterpret_cast<base::char16*>(entry) >= input);
+ DCHECK(reinterpret_cast<base::char16*>(entry) - input <
+ sizeof(size_t) / sizeof(base::char16));
}
// Clean up the memory we copied over.
@@ -78,7 +78,7 @@ void HandleCloserAgent::InitializeHandlesToClose() {
bool HandleCloserAgent::CloseHandles() {
DWORD handle_count = UINT_MAX;
const int kInvalidHandleThreshold = 100;
- const size_t kHandleOffset = sizeof(HANDLE);
+ const size_t kHandleOffset = 4; // Handles are always a multiple of 4.
if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
return false;
@@ -88,7 +88,7 @@ bool HandleCloserAgent::CloseHandles() {
32 * sizeof(wchar_t));
OBJECT_TYPE_INFORMATION* type_info =
reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
- string16 handle_name;
+ base::string16 handle_name;
HANDLE handle = NULL;
int invalid_count = 0;
diff --git a/chromium/sandbox/win/src/handle_closer_test.cc b/chromium/sandbox/win/src/handle_closer_test.cc
index ba0e33a2204..7fa259889d8 100644
--- a/chromium/sandbox/win/src/handle_closer_test.cc
+++ b/chromium/sandbox/win/src/handle_closer_test.cc
@@ -19,7 +19,7 @@ const wchar_t *kFileExtensions[] = { L".1", L".2", L".3", L".4" };
HANDLE GetMarkerFile(const wchar_t *extension) {
wchar_t path_buffer[MAX_PATH + 1];
CHECK(::GetTempPath(MAX_PATH, path_buffer));
- string16 marker_path = path_buffer;
+ base::string16 marker_path = path_buffer;
marker_path += L"\\sbox_marker_";
// Generate a unique value from the exe's size and timestamp.
@@ -73,10 +73,10 @@ SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) {
// Brute force the handle table to find what we're looking for.
DWORD handle_count = UINT_MAX;
const int kInvalidHandleThreshold = 100;
- const size_t kHandleOffset = sizeof(HANDLE);
+ const size_t kHandleOffset = 4; // Handles are always a multiple of 4.
HANDLE handle = NULL;
int invalid_count = 0;
- string16 handle_name;
+ base::string16 handle_name;
if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
return SBOX_TEST_FAILED_TO_RUN_TEST;
@@ -110,9 +110,9 @@ TEST(HandleCloserTest, CheckForMarkerFiles) {
runner.SetTestState(EVERY_STATE);
sandbox::TargetPolicy* policy = runner.GetPolicy();
- string16 command = string16(L"CheckForFileHandles Y");
+ base::string16 command = base::string16(L"CheckForFileHandles Y");
for (int i = 0; i < arraysize(kFileExtensions); ++i) {
- string16 handle_name;
+ base::string16 handle_name;
base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
CHECK(marker.IsValid());
CHECK(sandbox::GetHandleName(marker, &handle_name));
@@ -130,9 +130,9 @@ TEST(HandleCloserTest, CloseMarkerFiles) {
runner.SetTestState(EVERY_STATE);
sandbox::TargetPolicy* policy = runner.GetPolicy();
- string16 command = string16(L"CheckForFileHandles N");
+ base::string16 command = base::string16(L"CheckForFileHandles N");
for (int i = 0; i < arraysize(kFileExtensions); ++i) {
- string16 handle_name;
+ base::string16 handle_name;
base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
CHECK(marker.IsValid());
CHECK(sandbox::GetHandleName(marker, &handle_name));
diff --git a/chromium/sandbox/win/src/handle_dispatcher.cc b/chromium/sandbox/win/src/handle_dispatcher.cc
index 26b8fc365ad..6acb6f9ceb3 100644
--- a/chromium/sandbox/win/src/handle_dispatcher.cc
+++ b/chromium/sandbox/win/src/handle_dispatcher.cc
@@ -53,10 +53,11 @@ bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc,
HANDLE handle_temp;
if (!::DuplicateHandle(ipc->client_info->process, source_handle,
::GetCurrentProcess(), &handle_temp,
- 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ 0, FALSE, DUPLICATE_SAME_ACCESS | options)) {
ipc->return_info.win32_result = ::GetLastError();
return false;
}
+ options &= ~DUPLICATE_CLOSE_SOURCE;
base::win::ScopedHandle handle(handle_temp);
// Get the object type (32 characters is safe; current max is 14).
@@ -78,8 +79,7 @@ bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc,
EvalResult eval = policy_base_->EvalPolicy(IPC_DUPLICATEHANDLEPROXY_TAG,
params.GetBase());
ipc->return_info.win32_result =
- HandlePolicy::DuplicateHandleProxyAction(eval, *ipc->client_info,
- source_handle,
+ HandlePolicy::DuplicateHandleProxyAction(eval, handle,
target_process_id,
&ipc->return_info.handle,
desired_access, options);
diff --git a/chromium/sandbox/win/src/handle_policy.cc b/chromium/sandbox/win/src/handle_policy.cc
index 718376ecee1..f5f1c273b96 100644
--- a/chromium/sandbox/win/src/handle_policy.cc
+++ b/chromium/sandbox/win/src/handle_policy.cc
@@ -52,7 +52,6 @@ bool HandlePolicy::GenerateRules(const wchar_t* type_name,
}
DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result,
- const ClientInfo& client_info,
HANDLE source_handle,
DWORD target_process_id,
HANDLE* target_handle,
@@ -81,7 +80,7 @@ DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result,
HANDLE target_process = remote_target_process.IsValid() ?
remote_target_process.Get() : ::GetCurrentProcess();
DWORD result = ERROR_SUCCESS;
- if (!::DuplicateHandle(client_info.process, source_handle, target_process,
+ if (!::DuplicateHandle(::GetCurrentProcess(), source_handle, target_process,
target_handle, desired_access, FALSE,
options)) {
return ::GetLastError();
diff --git a/chromium/sandbox/win/src/handle_policy.h b/chromium/sandbox/win/src/handle_policy.h
index d91a0393aef..ffe54b8ae2e 100644
--- a/chromium/sandbox/win/src/handle_policy.h
+++ b/chromium/sandbox/win/src/handle_policy.h
@@ -27,7 +27,6 @@ class HandlePolicy {
// Processes a 'TargetPolicy::DuplicateHandle()' request from the target.
static DWORD DuplicateHandleProxyAction(EvalResult eval_result,
- const ClientInfo& client_info,
HANDLE source_handle,
DWORD target_process_id,
HANDLE* target_handle,
diff --git a/chromium/sandbox/win/src/handle_table.cc b/chromium/sandbox/win/src/handle_table.cc
index 7230dff87b5..3b2febe6251 100644
--- a/chromium/sandbox/win/src/handle_table.cc
+++ b/chromium/sandbox/win/src/handle_table.cc
@@ -22,22 +22,22 @@ bool CompareHandleEntries(const SYSTEM_HANDLE_INFORMATION& a,
namespace sandbox {
-const char16* HandleTable::kTypeProcess = L"Process";
-const char16* HandleTable::kTypeThread = L"Thread";
-const char16* HandleTable::kTypeFile = L"File";
-const char16* HandleTable::kTypeDirectory = L"Directory";
-const char16* HandleTable::kTypeKey = L"Key";
-const char16* HandleTable::kTypeWindowStation = L"WindowStation";
-const char16* HandleTable::kTypeDesktop = L"Desktop";
-const char16* HandleTable::kTypeService = L"Service";
-const char16* HandleTable::kTypeMutex = L"Mutex";
-const char16* HandleTable::kTypeSemaphore = L"Semaphore";
-const char16* HandleTable::kTypeEvent = L"Event";
-const char16* HandleTable::kTypeTimer = L"Timer";
-const char16* HandleTable::kTypeNamedPipe = L"NamedPipe";
-const char16* HandleTable::kTypeJobObject = L"JobObject";
-const char16* HandleTable::kTypeFileMap = L"FileMap";
-const char16* HandleTable::kTypeAlpcPort = L"ALPC Port";
+const base::char16* HandleTable::kTypeProcess = L"Process";
+const base::char16* HandleTable::kTypeThread = L"Thread";
+const base::char16* HandleTable::kTypeFile = L"File";
+const base::char16* HandleTable::kTypeDirectory = L"Directory";
+const base::char16* HandleTable::kTypeKey = L"Key";
+const base::char16* HandleTable::kTypeWindowStation = L"WindowStation";
+const base::char16* HandleTable::kTypeDesktop = L"Desktop";
+const base::char16* HandleTable::kTypeService = L"Service";
+const base::char16* HandleTable::kTypeMutex = L"Mutex";
+const base::char16* HandleTable::kTypeSemaphore = L"Semaphore";
+const base::char16* HandleTable::kTypeEvent = L"Event";
+const base::char16* HandleTable::kTypeTimer = L"Timer";
+const base::char16* HandleTable::kTypeNamedPipe = L"NamedPipe";
+const base::char16* HandleTable::kTypeJobObject = L"JobObject";
+const base::char16* HandleTable::kTypeFileMap = L"FileMap";
+const base::char16* HandleTable::kTypeAlpcPort = L"ALPC Port";
HandleTable::HandleTable() {
static NtQuerySystemInformation QuerySystemInformation = NULL;
@@ -151,17 +151,17 @@ const OBJECT_TYPE_INFORMATION* HandleTable::HandleEntry::TypeInfo() {
return type_info_buffer_.empty() ? NULL : type_info_internal();
}
-const string16& HandleTable::HandleEntry::Name() {
+const base::string16& HandleTable::HandleEntry::Name() {
UpdateInfo(UPDATE_INFO_AND_NAME);
return handle_name_;
}
-const string16& HandleTable::HandleEntry::Type() {
+const base::string16& HandleTable::HandleEntry::Type() {
UpdateInfo(UPDATE_INFO_AND_TYPE_NAME);
return type_name_;
}
-bool HandleTable::HandleEntry::IsType(const string16& type_string) {
+bool HandleTable::HandleEntry::IsType(const base::string16& type_string) {
UpdateInfo(UPDATE_INFO_ONLY);
if (type_info_buffer_.empty())
return false;
diff --git a/chromium/sandbox/win/src/handle_table.h b/chromium/sandbox/win/src/handle_table.h
index 21ff80ff481..1b553fae3aa 100644
--- a/chromium/sandbox/win/src/handle_table.h
+++ b/chromium/sandbox/win/src/handle_table.h
@@ -18,22 +18,22 @@ namespace sandbox {
// for iterating through the table and retrieving handle info.
class HandleTable {
public:
- static const char16* HandleTable::kTypeProcess;
- static const char16* HandleTable::kTypeThread;
- static const char16* HandleTable::kTypeFile;
- static const char16* HandleTable::kTypeDirectory;
- static const char16* HandleTable::kTypeKey;
- static const char16* HandleTable::kTypeWindowStation;
- static const char16* HandleTable::kTypeDesktop;
- static const char16* HandleTable::kTypeService;
- static const char16* HandleTable::kTypeMutex;
- static const char16* HandleTable::kTypeSemaphore;
- static const char16* HandleTable::kTypeEvent;
- static const char16* HandleTable::kTypeTimer;
- static const char16* HandleTable::kTypeNamedPipe;
- static const char16* HandleTable::kTypeJobObject;
- static const char16* HandleTable::kTypeFileMap;
- static const char16* HandleTable::kTypeAlpcPort;
+ static const base::char16* HandleTable::kTypeProcess;
+ static const base::char16* HandleTable::kTypeThread;
+ static const base::char16* HandleTable::kTypeFile;
+ static const base::char16* HandleTable::kTypeDirectory;
+ static const base::char16* HandleTable::kTypeKey;
+ static const base::char16* HandleTable::kTypeWindowStation;
+ static const base::char16* HandleTable::kTypeDesktop;
+ static const base::char16* HandleTable::kTypeService;
+ static const base::char16* HandleTable::kTypeMutex;
+ static const base::char16* HandleTable::kTypeSemaphore;
+ static const base::char16* HandleTable::kTypeEvent;
+ static const base::char16* HandleTable::kTypeTimer;
+ static const base::char16* HandleTable::kTypeNamedPipe;
+ static const base::char16* HandleTable::kTypeJobObject;
+ static const base::char16* HandleTable::kTypeFileMap;
+ static const base::char16* HandleTable::kTypeAlpcPort;
class Iterator;
@@ -54,11 +54,11 @@ class HandleTable {
const OBJECT_TYPE_INFORMATION* TypeInfo();
- const string16& Name();
+ const base::string16& Name();
- const string16& Type();
+ const base::string16& Type();
- bool IsType(const string16& type_string);
+ bool IsType(const base::string16& type_string);
private:
friend class Iterator;
@@ -84,8 +84,8 @@ class HandleTable {
const SYSTEM_HANDLE_INFORMATION* handle_entry_;
const SYSTEM_HANDLE_INFORMATION* last_entry_;
std::vector<BYTE> type_info_buffer_;
- string16 handle_name_;
- string16 type_name_;
+ base::string16 handle_name_;
+ base::string16 type_name_;
DISALLOW_COPY_AND_ASSIGN(HandleEntry);
};
diff --git a/chromium/sandbox/win/src/interception.cc b/chromium/sandbox/win/src/interception.cc
index dde585735f1..5439db65b53 100644
--- a/chromium/sandbox/win/src/interception.cc
+++ b/chromium/sandbox/win/src/interception.cc
@@ -17,7 +17,6 @@
#include "sandbox/win/src/interception_internal.h"
#include "sandbox/win/src/interceptors.h"
#include "sandbox/win/src/sandbox.h"
-#include "sandbox/win/src/sandbox_utils.h"
#include "sandbox/win/src/service_resolver.h"
#include "sandbox/win/src/target_interceptions.h"
#include "sandbox/win/src/target_process.h"
@@ -399,7 +398,7 @@ bool InterceptionManager::PatchNtdll(bool hot_patch_needed) {
thunk_offset &= kPageSize - 1;
// Make an aligned, padded allocation, and move the pointer to our chunk.
- size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & kPageSize;
+ size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1);
thunk_base = reinterpret_cast<BYTE*>(
::VirtualAllocEx(child, thunk_base, thunk_bytes_padded,
MEM_COMMIT, PAGE_EXECUTE_READWRITE));
@@ -483,8 +482,6 @@ bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks,
thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_);
else
thunk = new Wow64ResolverThunk(child_->Process(), relaxed_);
- } else if (!IsXPSP2OrLater()) {
- thunk = new Win2kResolverThunk(child_->Process(), relaxed_);
} else if (os_info->version() >= base::win::VERSION_WIN8) {
thunk = new Win8ResolverThunk(child_->Process(), relaxed_);
} else {
diff --git a/chromium/sandbox/win/src/interceptors.h b/chromium/sandbox/win/src/interceptors.h
index 43126d005ac..a17447aa18c 100644
--- a/chromium/sandbox/win/src/interceptors.h
+++ b/chromium/sandbox/win/src/interceptors.h
@@ -41,9 +41,10 @@ enum InterceptorId {
// Sync dispatcher:
CREATE_EVENT_ID,
OPEN_EVENT_ID,
- // CSRSS bypasses for HandleCloser:
- CREATE_THREAD_ID,
- GET_USER_DEFAULT_LCID_ID,
+ // Process mitigations Win32k dispatcher:
+ GDIINITIALIZE_ID,
+ GETSTOCKOBJECT_ID,
+ REGISTERCLASSW_ID,
INTERCEPTOR_MAX_ID
};
diff --git a/chromium/sandbox/win/src/interceptors_64.cc b/chromium/sandbox/win/src/interceptors_64.cc
index c71d5a2803d..ef0b5f0017a 100644
--- a/chromium/sandbox/win/src/interceptors_64.cc
+++ b/chromium/sandbox/win/src/interceptors_64.cc
@@ -8,6 +8,7 @@
#include "sandbox/win/src/filesystem_interception.h"
#include "sandbox/win/src/named_pipe_interception.h"
#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
#include "sandbox/win/src/process_thread_interception.h"
#include "sandbox/win/src/registry_interception.h"
#include "sandbox/win/src/sandbox_nt_types.h"
@@ -68,23 +69,6 @@ NTSTATUS WINAPI TargetNtOpenThreadTokenEx64(
open_as_self, handle_attributes, token);
}
-HANDLE WINAPI TargetCreateThread64(
- LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size,
- LPTHREAD_START_ROUTINE start_address, PVOID parameter, DWORD creation_flags,
- LPDWORD thread_id) {
- CreateThreadFunction orig_fn = reinterpret_cast<
- CreateThreadFunction>(g_originals[CREATE_THREAD_ID]);
- return TargetCreateThread(orig_fn, thread_attributes, stack_size,
- start_address, parameter, creation_flags,
- thread_id);
-}
-
-LCID WINAPI TargetGetUserDefaultLCID64(void) {
- GetUserDefaultLCIDFunction orig_fn = reinterpret_cast<
- GetUserDefaultLCIDFunction>(g_originals[GET_USER_DEFAULT_LCID_ID]);
- return TargetGetUserDefaultLCID(orig_fn);
-}
-
// -----------------------------------------------------------------------
SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile64(
@@ -268,4 +252,27 @@ SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent64(
object_attributes);
}
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(
+ HANDLE dll,
+ DWORD reason) {
+ GdiDllInitializeFunction orig_fn = reinterpret_cast<
+ GdiDllInitializeFunction>(g_originals[GDIINITIALIZE_ID]);
+ return TargetGdiDllInitialize(orig_fn, dll, reason);
+}
+
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object) {
+ GetStockObjectFunction orig_fn = reinterpret_cast<
+ GetStockObjectFunction>(g_originals[GETSTOCKOBJECT_ID]);
+ return TargetGetStockObject(orig_fn, object);
+}
+
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW64(
+ const WNDCLASS* wnd_class) {
+ RegisterClassWFunction orig_fn = reinterpret_cast<
+ RegisterClassWFunction>(g_originals[REGISTERCLASSW_ID]);
+ return TargetRegisterClassW(orig_fn, wnd_class);
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/interceptors_64.h b/chromium/sandbox/win/src/interceptors_64.h
index ef2c10d412c..7368ceb8450 100644
--- a/chromium/sandbox/win/src/interceptors_64.h
+++ b/chromium/sandbox/win/src/interceptors_64.h
@@ -44,15 +44,6 @@ SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx64(
HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self,
ULONG handle_attributes, PHANDLE token);
-// Interception of CreateThread on the child process.
-SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateThread64(
- LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size,
- LPTHREAD_START_ROUTINE start_address, PVOID parameter,
- DWORD creation_flags, LPDWORD thread_id);
-
-// Interception of GetUserDefaultLCID on the child process.
-SANDBOX_INTERCEPT LCID WINAPI TargetGetUserDefaultLCID64();
-
// -----------------------------------------------------------------------
// Interceptors handled by the file system dispatcher.
@@ -163,6 +154,20 @@ SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent64(
PHANDLE event_handle, ACCESS_MASK desired_access,
POBJECT_ATTRIBUTES object_attributes);
+// -----------------------------------------------------------------------
+// Interceptors handled by the process mitigations win32k lockdown code.
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(
+ HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW64(const WNDCLASS* wnd_class);
+
} // extern "C"
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/ipc_tags.h b/chromium/sandbox/win/src/ipc_tags.h
index 4e3a8061d86..d680411dace 100644
--- a/chromium/sandbox/win/src/ipc_tags.h
+++ b/chromium/sandbox/win/src/ipc_tags.h
@@ -29,6 +29,9 @@ enum {
IPC_NTCREATEKEY_TAG,
IPC_NTOPENKEY_TAG,
IPC_DUPLICATEHANDLEPROXY_TAG,
+ IPC_GDI_GDIDLLINITIALIZE_TAG,
+ IPC_GDI_GETSTOCKOBJECT_TAG,
+ IPC_USER_REGISTERCLASSW_TAG,
IPC_LAST_TAG
};
diff --git a/chromium/sandbox/win/src/ipc_unittest.cc b/chromium/sandbox/win/src/ipc_unittest.cc
index 53b870c3f15..7bdd286d399 100644
--- a/chromium/sandbox/win/src/ipc_unittest.cc
+++ b/chromium/sandbox/win/src/ipc_unittest.cc
@@ -159,7 +159,7 @@ TEST(IPCTest, CrossCallStrPacking) {
CrossCallReturn answer;
uint32 tag1 = 666;
- const wchar_t text[] = L"98765 - 43210";
+ const wchar_t *text = L"98765 - 43210";
base::string16 copied_text;
CrossCallParamsEx* actual_params;
@@ -206,7 +206,7 @@ TEST(IPCTest, CrossCallStrPacking) {
param_size = 1;
base::string16 copied_text_p0, copied_text_p2;
- const wchar_t text2[] = L"AeFG";
+ const wchar_t *text2 = L"AeFG";
CrossCall(client, tag1, text2, null_text, text, &answer);
actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
EXPECT_EQ(3, actual_params->GetParamsCount());
@@ -235,7 +235,7 @@ TEST(IPCTest, CrossCallIntPacking) {
uint32 tag1 = 999;
uint32 tag2 = 111;
- const wchar_t text[] = L"godzilla";
+ const wchar_t *text = L"godzilla";
CrossCallParamsEx* actual_params;
char* mem = reinterpret_cast<char*>(client_control);
@@ -315,8 +315,7 @@ TEST(IPCTest, CrossCallValidation) {
EXPECT_EQ(1, ccp->GetParamsCount());
delete[] (reinterpret_cast<char*>(ccp));
-#if defined(NDEBUG)
- // Test hat we handle integer overflow on the number of params
+ // Test that we handle integer overflow on the number of params
// correctly. We use a test-only ctor for ActualCallParams that
// allows to create malformed cross-call buffers.
const int32 kPtrDiffSz = sizeof(ptrdiff_t);
@@ -332,7 +331,6 @@ TEST(IPCTest, CrossCallValidation) {
// If the buffer is malformed the return is NULL.
EXPECT_TRUE(NULL == ccp);
}
-#endif // defined(NDEBUG)
ActualCallParams<1, kBufferSize> params_3(kTag, 1);
params_3.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
diff --git a/chromium/sandbox/win/src/job.cc b/chromium/sandbox/win/src/job.cc
index 060ffa52438..8852ab0c720 100644
--- a/chromium/sandbox/win/src/job.cc
+++ b/chromium/sandbox/win/src/job.cc
@@ -14,8 +14,10 @@ Job::~Job() {
::CloseHandle(job_handle_);
};
-DWORD Job::Init(JobLevel security_level, wchar_t *job_name,
- DWORD ui_exceptions) {
+DWORD Job::Init(JobLevel security_level,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit) {
if (job_handle_)
return ERROR_ALREADY_INITIALIZED;
@@ -51,11 +53,11 @@ DWORD Job::Init(JobLevel security_level, wchar_t *job_name,
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
}
case JOB_UNPROTECTED: {
- // The JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag is not supported on
- // Windows 2000. We need a mechanism on Windows 2000 to ensure
- // that processes in the job are terminated when the job is closed
- if (base::win::GetVersion() == base::win::VERSION_PRE_XP)
- break;
+ if (memory_limit) {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY;
+ jeli.ProcessMemoryLimit = memory_limit;
+ }
jeli.BasicLimitInformation.LimitFlags |=
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
diff --git a/chromium/sandbox/win/src/job.h b/chromium/sandbox/win/src/job.h
index 487722f0bb2..60dc3146b70 100644
--- a/chromium/sandbox/win/src/job.h
+++ b/chromium/sandbox/win/src/job.h
@@ -29,7 +29,10 @@ class Job {
// If the function succeeds, the return value is ERROR_SUCCESS. If the
// function fails, the return value is the win32 error code corresponding to
// the error.
- DWORD Init(JobLevel security_level, wchar_t *job_name, DWORD ui_exceptions);
+ DWORD Init(JobLevel security_level,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit);
// Assigns the process referenced by process_handle to the job.
// If the function succeeds, the return value is ERROR_SUCCESS. If the
diff --git a/chromium/sandbox/win/src/job_unittest.cc b/chromium/sandbox/win/src/job_unittest.cc
index 3b2b331046c..a1b7acafe3e 100644
--- a/chromium/sandbox/win/src/job_unittest.cc
+++ b/chromium/sandbox/win/src/job_unittest.cc
@@ -16,7 +16,7 @@ TEST(JobTest, TestCreation) {
{
// Create the job.
Job job;
- ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
// check if the job exists.
HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE,
@@ -40,7 +40,7 @@ TEST(JobTest, TestDetach) {
{
// Create the job.
Job job;
- ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
job_handle = job.Detach();
ASSERT_TRUE(job_handle != NULL);
@@ -73,7 +73,7 @@ TEST(JobTest, TestExceptions) {
// Create the job.
Job job;
ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name",
- JOB_OBJECT_UILIMIT_READCLIPBOARD));
+ JOB_OBJECT_UILIMIT_READCLIPBOARD, 0));
job_handle = job.Detach();
ASSERT_TRUE(job_handle != NULL);
@@ -93,7 +93,7 @@ TEST(JobTest, TestExceptions) {
{
// Create the job.
Job job;
- ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
job_handle = job.Detach();
ASSERT_TRUE(job_handle != NULL);
@@ -115,8 +115,8 @@ TEST(JobTest, TestExceptions) {
TEST(JobTest, DoubleInit) {
// Create the job.
Job job;
- ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
- ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+ ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0, 0));
}
// Tests the error case when we use a method and the object is not yet
@@ -131,34 +131,35 @@ TEST(JobTest, NoInit) {
// Tests the initialization of the job with different security level.
TEST(JobTest, SecurityLevel) {
Job job1;
- ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0, 0));
Job job2;
- ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0, 0));
Job job3;
- ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0, 0));
Job job4;
- ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0, 0));
Job job5;
- ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0, 0));
// JOB_NONE means we run without a job object so Init should fail.
Job job6;
- ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init(JOB_NONE, L"job6", 0));
+ ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init(JOB_NONE, L"job6", 0, 0));
Job job7;
ASSERT_EQ(ERROR_BAD_ARGUMENTS, job7.Init(
- static_cast<JobLevel>(JOB_NONE+1), L"job7", 0));
+ static_cast<JobLevel>(JOB_NONE+1), L"job7", 0, 0));
}
// Tests the method "AssignProcessToJob".
TEST(JobTest, ProcessInJob) {
// Create the job.
Job job;
- ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0));
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0,
+ 0));
BOOL result = FALSE;
diff --git a/chromium/sandbox/win/src/named_pipe_dispatcher.cc b/chromium/sandbox/win/src/named_pipe_dispatcher.cc
index da4045c2745..daf88f805dc 100644
--- a/chromium/sandbox/win/src/named_pipe_dispatcher.cc
+++ b/chromium/sandbox/win/src/named_pipe_dispatcher.cc
@@ -55,9 +55,9 @@ bool NamedPipeDispatcher::CreateNamedPipe(
iter != paths.end(); ++iter) {
base::SplitString(*iter, '\\', &innerpaths);
for (std::vector<base::string16>::const_iterator iter2 = innerpaths.begin();
- iter2 != innerpaths.end(); ++iter2) {
+ iter2 != innerpaths.end(); ++iter2) {
if (*iter2 == L"..")
- return true;
+ return true;
}
}
diff --git a/chromium/sandbox/win/src/named_pipe_policy_test.cc b/chromium/sandbox/win/src/named_pipe_policy_test.cc
index fe8c71f70b9..813cf1f4640 100644
--- a/chromium/sandbox/win/src/named_pipe_policy_test.cc
+++ b/chromium/sandbox/win/src/named_pipe_policy_test.cc
@@ -109,9 +109,10 @@ TEST(NamedPipePolicyTest, CreatePipeCanonicalization) {
// 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));
+ const wchar_t* argv[2] = { L"\\\\?\\pipe\\test\\..\\bleh",
+ L"\\Device\\NamedPipe\\test" };
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ NamedPipe_Create(2, const_cast<wchar_t**>(argv)));
}
// The same test as CreatePipe but this time using strict interceptions.
diff --git a/chromium/sandbox/win/src/nt_internals.h b/chromium/sandbox/win/src/nt_internals.h
index e0c74ac1fb4..8b22e0e9a3d 100644
--- a/chromium/sandbox/win/src/nt_internals.h
+++ b/chromium/sandbox/win/src/nt_internals.h
@@ -25,6 +25,7 @@ typedef LONG NTSTATUS;
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
+#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL)
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
@@ -125,6 +126,15 @@ typedef struct _IO_STATUS_BLOCK {
#define FILE_OPEN_NO_RECALL 0x00400000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
+// Create/open result values. These are the disposition values returned on the
+// io status information.
+#define FILE_SUPERSEDED 0x00000000
+#define FILE_OPENED 0x00000001
+#define FILE_CREATED 0x00000002
+#define FILE_OVERWRITTEN 0x00000003
+#define FILE_EXISTS 0x00000004
+#define FILE_DOES_NOT_EXIST 0x00000005
+
typedef NTSTATUS (WINAPI *NtCreateFileFunction)(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.cc b/chromium/sandbox/win/src/policy_engine_opcodes.cc
index e8a39ed6122..5a03ea13594 100644
--- a/chromium/sandbox/win/src/policy_engine_opcodes.cc
+++ b/chromium/sandbox/win/src/policy_engine_opcodes.cc
@@ -162,7 +162,7 @@ PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param,
unsigned long upper_bound,
uint32 options) {
if (lower_bound > upper_bound) {
- return false;
+ return NULL;
}
PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options,
selected_param);
diff --git a/chromium/sandbox/win/src/policy_engine_unittest.cc b/chromium/sandbox/win/src/policy_engine_unittest.cc
index e6c343500fe..f96ff37d9f1 100644
--- a/chromium/sandbox/win/src/policy_engine_unittest.cc
+++ b/chromium/sandbox/win/src/policy_engine_unittest.cc
@@ -60,7 +60,7 @@ TEST(PolicyEngineTest, Rules1) {
opcode_maker.MakeOpAction(FAKE_ACCESS_DENIED, kPolNone);
policy->opcode_count = 7;
- wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
unsigned long creation_mode = OPEN_EXISTING;
unsigned long flags = FILE_ATTRIBUTE_NORMAL;
void* security_descriptor = NULL;
diff --git a/chromium/sandbox/win/src/policy_low_level_unittest.cc b/chromium/sandbox/win/src/policy_low_level_unittest.cc
index 2b5d0f7a6ec..c9424c6cf4f 100644
--- a/chromium/sandbox/win/src/policy_low_level_unittest.cc
+++ b/chromium/sandbox/win/src/policy_low_level_unittest.cc
@@ -62,7 +62,7 @@ TEST(PolicyEngineTest, SimpleStrMatch) {
EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = L"Z:\\Directory\\domo.txt";
+ const wchar_t* filename = L"Z:\\Directory\\domo.txt";
POLPARAMS_BEGIN(eval_params)
POLPARAM(filename) // Argument 0
@@ -95,7 +95,7 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatch) {
EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = NULL;
+ const wchar_t* filename = NULL;
POLPARAMS_BEGIN(eval_params)
POLPARAM(filename) // Argument 0
POLPARAMS_END;
@@ -133,7 +133,7 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) {
EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = NULL;
+ const wchar_t* filename = NULL;
POLPARAMS_BEGIN(eval_params)
POLPARAM(filename) // Argument 0
POLPARAMS_END;
@@ -166,7 +166,7 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) {
EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = NULL;
+ const wchar_t* filename = NULL;
POLPARAMS_BEGIN(eval_params)
POLPARAM(filename) // Argument 0
POLPARAMS_END;
@@ -205,7 +205,7 @@ TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) {
EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = NULL;
+ const wchar_t* filename = NULL;
unsigned long access = 0;
POLPARAMS_BEGIN(eval_params)
POLPARAM(filename) // Argument 0
@@ -254,7 +254,7 @@ TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) {
EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = NULL;
+ const wchar_t* filename = NULL;
unsigned long access = 0;
unsigned long sharing = 66;
@@ -328,7 +328,7 @@ TEST(PolicyEngineTest, OneRuleTest) {
EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr));
EXPECT_TRUE(policyGen.Done());
- wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
unsigned long creation_mode = OPEN_EXISTING;
unsigned long flags = FILE_ATTRIBUTE_NORMAL;
void* security_descriptor = NULL;
@@ -484,7 +484,7 @@ TEST(PolicyEngineTest, ThreeRulesTest) {
// Test the policy evaluation.
- wchar_t* filename = L"";
+ const wchar_t* filename = L"";
unsigned long creation_mode = OPEN_EXISTING;
unsigned long flags = FILE_ATTRIBUTE_NORMAL;
void* security_descriptor = NULL;
@@ -588,7 +588,7 @@ TEST(PolicyEngineTest, PolicyRuleCopyConstructorTwoStrings) {
EXPECT_TRUE(policyGen.AddRule(2, &pr_copy));
EXPECT_TRUE(policyGen.Done());
- wchar_t* name = NULL;
+ const wchar_t* name = NULL;
POLPARAMS_BEGIN(eval_params)
POLPARAM(name)
POLPARAMS_END;
diff --git a/chromium/sandbox/win/src/policy_opcodes_unittest.cc b/chromium/sandbox/win/src/policy_opcodes_unittest.cc
index e1a7ad63d20..c69aad8c84e 100644
--- a/chromium/sandbox/win/src/policy_opcodes_unittest.cc
+++ b/chromium/sandbox/win/src/policy_opcodes_unittest.cc
@@ -10,9 +10,9 @@
#define INIT_GLOBAL_RTL(member) \
- g_nt.##member = reinterpret_cast<##member##Function>( \
+ g_nt.member = reinterpret_cast<member##Function>( \
::GetProcAddress(ntdll, #member)); \
- if (NULL == g_nt.##member) \
+ if (NULL == g_nt.member) \
return false
namespace sandbox {
@@ -185,8 +185,7 @@ TEST(PolicyEngineTest, IntegerOpcodes) {
OpcodeFactory opcode_maker(memory, sizeof(memory));
// Test basic match for unsigned longs 42 == 42 and 42 != 113377.
- PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, unsigned long(42),
- kPolNone);
+ PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, 42UL, kPolNone);
EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, NULL));
EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, NULL));
EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, NULL));
diff --git a/chromium/sandbox/win/src/policy_target_test.cc b/chromium/sandbox/win/src/policy_target_test.cc
index dba670a9989..268f8d627a9 100644
--- a/chromium/sandbox/win/src/policy_target_test.cc
+++ b/chromium/sandbox/win/src/policy_target_test.cc
@@ -243,6 +243,7 @@ TEST(PolicyTargetTest, DesktopPolicy) {
PROCESS_INFORMATION temp_process_info = {};
result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
&temp_process_info);
+ base::string16 desktop_name = policy->GetAlternateDesktop();
policy->Release();
EXPECT_EQ(SBOX_ALL_OK, result);
@@ -256,7 +257,6 @@ TEST(PolicyTargetTest, DesktopPolicy) {
EXPECT_NE(::GetThreadDesktop(target.thread_id()),
::GetThreadDesktop(::GetCurrentThreadId()));
- 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));
@@ -306,6 +306,7 @@ TEST(PolicyTargetTest, WinstaPolicy) {
PROCESS_INFORMATION temp_process_info = {};
result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
&temp_process_info);
+ base::string16 desktop_name = policy->GetAlternateDesktop();
policy->Release();
EXPECT_EQ(SBOX_ALL_OK, result);
@@ -319,7 +320,6 @@ TEST(PolicyTargetTest, WinstaPolicy) {
EXPECT_NE(::GetThreadDesktop(target.thread_id()),
::GetThreadDesktop(::GetCurrentThreadId()));
- base::string16 desktop_name = policy->GetAlternateDesktop();
ASSERT_FALSE(desktop_name.empty());
// Make sure there is a backslash, for the window station name.
diff --git a/chromium/sandbox/win/src/process_mitigations.cc b/chromium/sandbox/win/src/process_mitigations.cc
index d1130475c35..5e242f3abfa 100644
--- a/chromium/sandbox/win/src/process_mitigations.cc
+++ b/chromium/sandbox/win/src/process_mitigations.cc
@@ -8,7 +8,6 @@
#include "base/win/windows_version.h"
#include "sandbox/win/src/nt_internals.h"
-#include "sandbox/win/src/sandbox_utils.h"
#include "sandbox/win/src/win_utils.h"
namespace {
@@ -32,10 +31,6 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) {
if (!CanSetProcessMitigationsPostStartup(flags))
return false;
- // We can't apply anything before Win XP, so just return cleanly.
- if (!IsXPSP2OrLater())
- return true;
-
base::win::Version version = base::win::GetVersion();
HMODULE module = ::GetModuleHandleA("kernel32.dll");
@@ -250,28 +245,23 @@ void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
}
MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags) {
- // Anything prior to XP SP2.
- if (!IsXPSP2OrLater())
- return 0;
-
base::win::Version version = base::win::GetVersion();
// Windows XP SP2+.
if (version < base::win::VERSION_VISTA) {
return flags & (MITIGATION_DEP |
MITIGATION_DEP_NO_ATL_THUNK);
+ }
// Windows Vista
if (version < base::win::VERSION_WIN7) {
- return flags & (MITIGATION_DEP |
- MITIGATION_DEP_NO_ATL_THUNK |
- MITIGATION_BOTTOM_UP_ASLR |
+ return flags & (MITIGATION_BOTTOM_UP_ASLR |
MITIGATION_DLL_SEARCH_ORDER |
MITIGATION_HEAP_TERMINATE);
}
- // Windows 7 and Vista.
- } else if (version < base::win::VERSION_WIN8) {
+ // Windows 7.
+ if (version < base::win::VERSION_WIN8) {
return flags & (MITIGATION_BOTTOM_UP_ASLR |
MITIGATION_DLL_SEARCH_ORDER |
MITIGATION_HEAP_TERMINATE);
@@ -318,7 +308,6 @@ bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) {
MITIGATION_RELOCATE_IMAGE_REQUIRED |
MITIGATION_BOTTOM_UP_ASLR |
MITIGATION_STRICT_HANDLE_CHECKS |
- MITIGATION_WIN32K_DISABLE |
MITIGATION_EXTENSION_DLL_DISABLE |
MITIGATION_DLL_SEARCH_ORDER));
}
@@ -326,7 +315,6 @@ bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) {
bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) {
// These mitigations cannot be enabled prior to startup.
return !(flags & (MITIGATION_STRICT_HANDLE_CHECKS |
- MITIGATION_WIN32K_DISABLE |
MITIGATION_DLL_SEARCH_ORDER));
}
diff --git a/chromium/sandbox/win/src/process_mitigations_test.cc b/chromium/sandbox/win/src/process_mitigations_test.cc
index f1274c54406..e6062564fe0 100644
--- a/chromium/sandbox/win/src/process_mitigations_test.cc
+++ b/chromium/sandbox/win/src/process_mitigations_test.cc
@@ -10,7 +10,6 @@
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/sandbox.h"
#include "sandbox/win/src/sandbox_factory.h"
-#include "sandbox/win/src/sandbox_utils.h"
#include "sandbox/win/src/target_services.h"
#include "sandbox/win/src/win_utils.h"
#include "sandbox/win/tests/common/controller.h"
@@ -89,7 +88,6 @@ SBOX_TESTS_COMMAND int CheckWin8(int argc, wchar_t **argv) {
reinterpret_cast<GetProcessMitigationPolicyFunction>(
::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
"GetProcessMitigationPolicy"));
-
if (!get_process_mitigation_policy)
return SBOX_TEST_NOT_FOUND;
@@ -104,9 +102,6 @@ SBOX_TESTS_COMMAND int CheckWin8(int argc, wchar_t **argv) {
if (!CheckWin8StrictHandlePolicy())
return SBOX_TEST_THIRD_ERROR;
- if (!CheckWin8Win32CallPolicy())
- return SBOX_TEST_FOURTH_ERROR;
-
if (!CheckWin8DllExtensionPolicy())
return SBOX_TEST_FIFTH_ERROR;
@@ -130,8 +125,7 @@ TEST(ProcessMitigationsTest, CheckWin8) {
EXPECT_EQ(policy->SetProcessMitigations(mitigations), SBOX_ALL_OK);
- mitigations |= MITIGATION_STRICT_HANDLE_CHECKS |
- MITIGATION_WIN32K_DISABLE;
+ mitigations |= MITIGATION_STRICT_HANDLE_CHECKS;
EXPECT_EQ(policy->SetDelayedProcessMitigations(mitigations), SBOX_ALL_OK);
@@ -188,7 +182,7 @@ SBOX_TESTS_COMMAND int CheckDep(int argc, wchar_t **argv) {
#if !defined(_WIN64) // DEP is always enabled on 64-bit.
TEST(ProcessMitigationsTest, CheckDep) {
- if (!IsXPSP2OrLater() || base::win::GetVersion() > base::win::VERSION_WIN7)
+ if (base::win::GetVersion() > base::win::VERSION_WIN7)
return;
TestRunner runner;
@@ -203,5 +197,52 @@ TEST(ProcessMitigationsTest, CheckDep) {
}
#endif
+SBOX_TESTS_COMMAND int CheckWin8Lockdown(int argc, wchar_t **argv) {
+ get_process_mitigation_policy =
+ reinterpret_cast<GetProcessMitigationPolicyFunction>(
+ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
+ "GetProcessMitigationPolicy"));
+ if (!get_process_mitigation_policy)
+ return SBOX_TEST_NOT_FOUND;
+
+ if (!CheckWin8Win32CallPolicy())
+ return SBOX_TEST_FIRST_ERROR;
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation on
+// the target process causes the launch to fail in process initialization.
+// The test process itself links against user32/gdi32.
+TEST(ProcessMitigationsTest, CheckWin8Win32KLockDownFailure) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE),
+ SBOX_ALL_OK);
+ EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown"));
+}
+
+// This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation
+// along with the policy to fake user32 and gdi32 initialization successfully
+// launches the target process.
+// The test process itself links against user32/gdi32.
+TEST(ProcessMitigationsTest, CheckWin8Win32KLockDownSuccess) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE),
+ SBOX_ALL_OK);
+ EXPECT_EQ(policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN,
+ sandbox::TargetPolicy::FAKE_USER_GDI_INIT, NULL),
+ sandbox::SBOX_ALL_OK);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown"));
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc b/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
new file mode 100644
index 00000000000..e426084f861
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
@@ -0,0 +1,57 @@
+// 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/win/src/process_mitigations_win32k_dispatcher.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+namespace sandbox {
+
+ProcessMitigationsWin32KDispatcher::ProcessMitigationsWin32KDispatcher(
+ PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+}
+
+bool ProcessMitigationsWin32KDispatcher::SetupService(
+ InterceptionManager* manager, int service) {
+ if (!(policy_base_->GetProcessMitigations() &
+ sandbox::MITIGATION_WIN32K_DISABLE)) {
+ return false;
+ }
+
+ switch (service) {
+ case IPC_GDI_GDIDLLINITIALIZE_TAG: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GdiDllInitialize,
+ GDIINITIALIZE_ID, 12)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IPC_GDI_GETSTOCKOBJECT_TAG: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetStockObject,
+ GETSTOCKOBJECT_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IPC_USER_REGISTERCLASSW_TAG: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", RegisterClassW,
+ REGISTERCLASSW_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ default:
+ break;
+ }
+ return false;
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h b/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h
new file mode 100644
index 00000000000..65c9f77e559
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h
@@ -0,0 +1,31 @@
+// 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_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class sets up intercepts for the Win32K lockdown policy which is set
+// on Windows 8 and beyond.
+class ProcessMitigationsWin32KDispatcher : public Dispatcher {
+ public:
+ explicit ProcessMitigationsWin32KDispatcher(PolicyBase* policy_base);
+ ~ProcessMitigationsWin32KDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+ private:
+ PolicyBase* policy_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMitigationsWin32KDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc b/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc
new file mode 100644
index 00000000000..ee24fbf434c
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc
@@ -0,0 +1,29 @@
+// 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/win/src/process_mitigations_win32k_interception.h"
+
+namespace sandbox {
+
+BOOL WINAPI TargetGdiDllInitialize(
+ GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason) {
+ return TRUE;
+}
+
+HGDIOBJ WINAPI TargetGetStockObject(
+ GetStockObjectFunction orig_get_stock_object,
+ int object) {
+ return reinterpret_cast<HGDIOBJ>(NULL);
+}
+
+ATOM WINAPI TargetRegisterClassW(
+ RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class) {
+ return TRUE;
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_interception.h b/chromium/sandbox/win/src/process_mitigations_win32k_interception.h
new file mode 100644
index 00000000000..bf7b551227a
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_interception.h
@@ -0,0 +1,46 @@
+// 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_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+typedef BOOL (WINAPI* GdiDllInitializeFunction) (
+ HANDLE dll,
+ DWORD reason,
+ LPVOID reserved);
+
+typedef HGDIOBJ (WINAPI *GetStockObjectFunction) (int object);
+
+typedef ATOM (WINAPI *RegisterClassWFunction) (const WNDCLASS* wnd_class);
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize(
+ GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject(
+ GetStockObjectFunction orig_get_stock_object,
+ int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW(
+ RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc b/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc
new file mode 100644
index 00000000000..af18c5413c2
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc
@@ -0,0 +1,24 @@
+// 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/win/src/process_mitigations_win32k_policy.h"
+
+namespace sandbox {
+
+bool ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule rule(FAKE_SUCCESS);
+ if (!policy->AddRule(IPC_GDI_GDIDLLINITIALIZE_TAG, &rule))
+ return false;
+ if (!policy->AddRule(IPC_GDI_GETSTOCKOBJECT_TAG, &rule))
+ return false;
+ if (!policy->AddRule(IPC_USER_REGISTERCLASSW_TAG, &rule))
+ return false;
+ return true;
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_policy.h b/chromium/sandbox/win/src/process_mitigations_win32k_policy.h
new file mode 100644
index 00000000000..078ed2bee15
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_policy.h
@@ -0,0 +1,35 @@
+// 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_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum EvalResult;
+
+// This class centralizes most of the knowledge related to the process
+// mitigations Win32K lockdown policy.
+class ProcessMitigationsWin32KLockdownPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for the Win32K process mitigation policy.
+ // name is the object name, semantics is the desired semantics for the
+ // open or create and policy is the policy generator to which the rules are
+ // going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+
+
diff --git a/chromium/sandbox/win/src/process_policy_test.cc b/chromium/sandbox/win/src/process_policy_test.cc
index a03e0bee5e0..af64f14f791 100644
--- a/chromium/sandbox/win/src/process_policy_test.cc
+++ b/chromium/sandbox/win/src/process_policy_test.cc
@@ -21,10 +21,10 @@ namespace {
// While the shell API provides better calls than this home brew function
// we use GetSystemWindowsDirectoryW which does not query the registry so
// it is safe to use after revert.
-string16 MakeFullPathToSystem32(const wchar_t* name) {
+base::string16 MakeFullPathToSystem32(const wchar_t* name) {
wchar_t windows_path[MAX_PATH] = {0};
::GetSystemWindowsDirectoryW(windows_path, MAX_PATH);
- string16 full_path(windows_path);
+ base::string16 full_path(windows_path);
if (full_path.empty()) {
return full_path;
}
@@ -35,8 +35,8 @@ string16 MakeFullPathToSystem32(const wchar_t* name) {
// Creates a process with the |exe| and |command| parameter using the
// unicode and ascii version of the api.
-sandbox::SboxTestResult CreateProcessHelper(const string16& exe,
- const string16& command) {
+sandbox::SboxTestResult CreateProcessHelper(const base::string16& exe,
+ const base::string16& command) {
base::win::ScopedProcessInformation pi;
STARTUPINFOW si = {sizeof(si)};
@@ -109,10 +109,10 @@ SBOX_TESTS_COMMAND int Process_RunApp1(int argc, wchar_t **argv) {
if ((NULL == argv) || (NULL == argv[0])) {
return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
}
- string16 path = MakeFullPathToSystem32(argv[0]);
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
// TEST 1: Try with the path in the app_name.
- return CreateProcessHelper(path, string16());
+ return CreateProcessHelper(path, base::string16());
}
SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t **argv) {
@@ -122,13 +122,13 @@ SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t **argv) {
if ((NULL == argv) || (NULL == argv[0])) {
return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
}
- string16 path = MakeFullPathToSystem32(argv[0]);
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
// TEST 2: Try with the path in the cmd_line.
- string16 cmd_line = L"\"";
+ base::string16 cmd_line = L"\"";
cmd_line += path;
cmd_line += L"\"";
- return CreateProcessHelper(string16(), cmd_line);
+ return CreateProcessHelper(base::string16(), cmd_line);
}
SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t **argv) {
@@ -140,7 +140,7 @@ SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t **argv) {
}
// TEST 3: Try file name in the cmd_line.
- return CreateProcessHelper(string16(), argv[0]);
+ return CreateProcessHelper(base::string16(), argv[0]);
}
SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t **argv) {
@@ -152,7 +152,7 @@ SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t **argv) {
}
// TEST 4: Try file name in the app_name and current directory sets correctly.
- string16 system32 = MakeFullPathToSystem32(L"");
+ base::string16 system32 = MakeFullPathToSystem32(L"");
wchar_t current_directory[MAX_PATH + 1];
int result4;
bool test_succeeded = false;
@@ -164,7 +164,7 @@ SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t **argv) {
current_directory[ret] = L'\\';
current_directory[ret+1] = L'\0';
if (::SetCurrentDirectory(system32.c_str())) {
- result4 = CreateProcessHelper(argv[0], string16());
+ result4 = CreateProcessHelper(argv[0], base::string16());
if (::SetCurrentDirectory(current_directory)) {
test_succeeded = true;
}
@@ -185,13 +185,13 @@ SBOX_TESTS_COMMAND int Process_RunApp5(int argc, wchar_t **argv) {
if ((NULL == argv) || (NULL == argv[0])) {
return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
}
- string16 path = MakeFullPathToSystem32(argv[0]);
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
// TEST 5: Try with the path in the cmd_line and arguments.
- string16 cmd_line = L"\"";
+ base::string16 cmd_line = L"\"";
cmd_line += path;
cmd_line += L"\" /I";
- return CreateProcessHelper(string16(), cmd_line);
+ return CreateProcessHelper(base::string16(), cmd_line);
}
SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t **argv) {
@@ -203,9 +203,9 @@ SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t **argv) {
}
// TEST 6: Try with the file_name in the cmd_line and arguments.
- string16 cmd_line = argv[0];
+ base::string16 cmd_line = argv[0];
cmd_line += L" /I";
- return CreateProcessHelper(string16(), cmd_line);
+ return CreateProcessHelper(base::string16(), cmd_line);
}
// Creates a process and checks if it's possible to get a handle to it's token.
@@ -216,7 +216,7 @@ SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) {
if ((NULL == argv) || (NULL == argv[0]))
return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
- string16 path = MakeFullPathToSystem32(argv[0]);
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
STARTUPINFOW si = {sizeof(si)};
@@ -284,8 +284,8 @@ TEST(ProcessPolicyTest, TestAllAccess) {
TEST(ProcessPolicyTest, CreateProcessAW) {
TestRunner runner;
- string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
- string16 system32 = MakeFullPathToSystem32(L"");
+ base::string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ base::string16 system32 = MakeFullPathToSystem32(L"");
ASSERT_TRUE(!exe_path.empty());
EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
TargetPolicy::PROCESS_MIN_EXEC,
@@ -339,7 +339,7 @@ TEST(ProcessPolicyTest, OpenToken) {
TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) {
TestRunner runner;
- string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ base::string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
ASSERT_TRUE(!exe_path.empty());
EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
TargetPolicy::PROCESS_MIN_EXEC,
@@ -351,7 +351,7 @@ TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) {
TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) {
TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE);
- string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ base::string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
ASSERT_TRUE(!exe_path.empty());
EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
TargetPolicy::PROCESS_ALL_EXEC,
@@ -363,7 +363,7 @@ TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) {
TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) {
TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
- string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ base::string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
ASSERT_TRUE(!exe_path.empty());
EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
TargetPolicy::PROCESS_MIN_EXEC,
@@ -375,7 +375,7 @@ TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) {
TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccessNoJob) {
TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
- string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ base::string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
ASSERT_TRUE(!exe_path.empty());
EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
TargetPolicy::PROCESS_ALL_EXEC,
diff --git a/chromium/sandbox/win/src/process_thread_interception.cc b/chromium/sandbox/win/src/process_thread_interception.cc
index d351ee546c8..45926bc5f63 100644
--- a/chromium/sandbox/win/src/process_thread_interception.cc
+++ b/chromium/sandbox/win/src/process_thread_interception.cc
@@ -400,50 +400,4 @@ BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
return FALSE;
}
-// Creates a thread without registering with CSRSS. This is required if we
-// closed the CSRSS ALPC port after lockdown.
-HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
- LPSECURITY_ATTRIBUTES thread_attributes,
- SIZE_T stack_size,
- LPTHREAD_START_ROUTINE start_address,
- PVOID parameter,
- DWORD creation_flags,
- LPDWORD thread_id) {
-// Try the normal CreateThread; switch to RtlCreateUserThread if needed.
- static bool use_create_thread = true;
- HANDLE thread;
- if (use_create_thread) {
- thread = orig_CreateThread(thread_attributes, stack_size, start_address,
- parameter, creation_flags, thread_id);
- if (thread)
- return thread;
- }
-
- PSECURITY_DESCRIPTOR sd =
- thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL;
- CLIENT_ID client_id;
-
- NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd,
- creation_flags & CREATE_SUSPENDED,
- 0, stack_size, 0, start_address,
- parameter, &thread, &client_id);
- if (!NT_SUCCESS(result))
- return 0;
-
- // CSRSS is closed if we got here, so use RtlCreateUserThread from here on.
- use_create_thread = false;
- if (thread_id)
- *thread_id = HandleToUlong(client_id.UniqueThread);
- return thread;
-}
-
-// Cache the default LCID to avoid pinging CSRSS after lockdown.
-// TODO(jschuh): This approach will miss a default locale changes after
-// lockdown. In the future we may want to have the broker check instead.
-LCID WINAPI TargetGetUserDefaultLCID(
- GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) {
- static LCID default_lcid = orig_GetUserDefaultLCID();
- return default_lcid;
-}
-
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_thread_interception.h b/chromium/sandbox/win/src/process_thread_interception.h
index 7d2d533dafd..31dc231543d 100644
--- a/chromium/sandbox/win/src/process_thread_interception.h
+++ b/chromium/sandbox/win/src/process_thread_interception.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2011 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.
@@ -83,17 +83,6 @@ SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA(
LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info,
LPPROCESS_INFORMATION process_information);
-// Interception of CreateThread in kernel32.dll.
-SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateThread(
- CreateThreadFunction orig_CreateThread,
- LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size,
- LPTHREAD_START_ROUTINE start_address, PVOID parameter,
- DWORD creation_flags, LPDWORD thread_id);
-
-// Interception of GetUserDefaultLCID in kernel32.dll.
-SANDBOX_INTERCEPT LCID WINAPI TargetGetUserDefaultLCID(
- GetUserDefaultLCIDFunction orig_GetUserDefaultLCID);
-
} // extern "C"
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_thread_policy.cc b/chromium/sandbox/win/src/process_thread_policy.cc
index 85a2f978a93..f2029ba6819 100644
--- a/chromium/sandbox/win/src/process_thread_policy.cc
+++ b/chromium/sandbox/win/src/process_thread_policy.cc
@@ -227,7 +227,8 @@ DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result,
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
- scoped_ptr_malloc<wchar_t> cmd_line(_wcsdup(command_line.c_str()));
+ scoped_ptr<wchar_t, base::FreeDeleter>
+ cmd_line(_wcsdup(command_line.c_str()));
BOOL should_give_full_access = (GIVE_ALLACCESS == eval_result);
if (!CreateProcessExWHelper(client_info.process, should_give_full_access,
diff --git a/chromium/sandbox/win/src/restricted_token.cc b/chromium/sandbox/win/src/restricted_token.cc
index 64973e98f32..3960926f4d7 100644
--- a/chromium/sandbox/win/src/restricted_token.cc
+++ b/chromium/sandbox/win/src/restricted_token.cc
@@ -14,7 +14,6 @@
namespace sandbox {
unsigned RestrictedToken::Init(const HANDLE effective_token) {
- DCHECK(!init_);
if (init_)
return ERROR_ALREADY_INITIALIZED;
diff --git a/chromium/sandbox/win/src/restricted_token_unittest.cc b/chromium/sandbox/win/src/restricted_token_unittest.cc
index 480106e8f35..8186f9c77c4 100644
--- a/chromium/sandbox/win/src/restricted_token_unittest.cc
+++ b/chromium/sandbox/win/src/restricted_token_unittest.cc
@@ -408,8 +408,8 @@ TEST(RestrictedTokenTest, DeletePrivilege) {
// elements in the restricting sids list has to be equal.
void CheckRestrictingSid(const ATL::CAccessToken &restricted_token,
ATL::CSid sid, int count) {
- DWORD length = 1000;
- BYTE *memory = new BYTE[1000];
+ DWORD length = 8192;
+ BYTE *memory = new BYTE[length];
TOKEN_GROUPS *groups = reinterpret_cast<TOKEN_GROUPS*>(memory);
ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(),
TokenRestrictedSids,
@@ -530,8 +530,8 @@ TEST(RestrictedTokenTest, AddMultipleRestrictingSids) {
ATL::CSid session;
restricted_token.GetLogonSid(&session);
- DWORD length = 1000;
- BYTE *memory = new BYTE[1000];
+ DWORD length = 8192;
+ BYTE *memory = new BYTE[length];
TOKEN_GROUPS *groups = reinterpret_cast<TOKEN_GROUPS*>(memory);
ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(),
TokenRestrictedSids,
@@ -577,9 +577,6 @@ TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) {
CheckRestrictingSid(restricted_token, user, -1);
}
-// Test to be executed only in release because they are triggering DCHECKs.
-#ifndef _DEBUG
-
// Checks the error code when the object is initialized twice.
TEST(RestrictedTokenTest, DoubleInit) {
RestrictedToken token;
@@ -588,6 +585,4 @@ TEST(RestrictedTokenTest, DoubleInit) {
ASSERT_EQ(ERROR_ALREADY_INITIALIZED, token.Init(NULL));
}
-#endif
-
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/restricted_token_utils.cc b/chromium/sandbox/win/src/restricted_token_utils.cc
index f3b18591e8f..93b212efaf3 100644
--- a/chromium/sandbox/win/src/restricted_token_utils.cc
+++ b/chromium/sandbox/win/src/restricted_token_utils.cc
@@ -146,7 +146,7 @@ DWORD StartRestrictedProcessInJob(wchar_t *command_line,
JobLevel job_level,
HANDLE *const job_handle_ret) {
Job job;
- DWORD err_code = job.Init(job_level, NULL, 0);
+ DWORD err_code = job.Init(job_level, NULL, 0, 0);
if (ERROR_SUCCESS != err_code)
return err_code;
diff --git a/chromium/sandbox/win/src/sandbox.cc b/chromium/sandbox/win/src/sandbox.cc
index d26daa434e9..984dfecec8a 100644
--- a/chromium/sandbox/win/src/sandbox.cc
+++ b/chromium/sandbox/win/src/sandbox.cc
@@ -12,8 +12,7 @@
namespace sandbox {
// The section for IPC and policy.
SANDBOX_INTERCEPT HANDLE g_shared_section;
-
-static bool s_is_broker = false;
+static bool s_is_broker = false;
// GetBrokerServices: the current implementation relies on a shared section
// that is created by the broker and opened by the target.
@@ -42,3 +41,8 @@ TargetServices* SandboxFactory::GetTargetServices() {
}
} // namespace sandbox
+
+// Allows querying for whether the current process has been sandboxed.
+extern "C" bool __declspec(dllexport) IsSandboxedProcess() {
+ return sandbox::g_shared_section != NULL;
+}
diff --git a/chromium/sandbox/win/src/sandbox_nt_util.cc b/chromium/sandbox/win/src/sandbox_nt_util.cc
index 613d4859dd7..ed1d908ad69 100644
--- a/chromium/sandbox/win/src/sandbox_nt_util.cc
+++ b/chromium/sandbox/win/src/sandbox_nt_util.cc
@@ -525,14 +525,17 @@ bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length,
if (file_info->RootDirectory)
return false;
+ static const wchar_t kPathPrefix[] = { L'\\', L'?', L'?', L'\\'};
+
// Check if it starts with \\??\\. We don't support relative paths.
- if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max)
+ if (file_info->FileNameLength < sizeof(kPathPrefix) ||
+ file_info->FileNameLength > kuint16max)
return false;
- if (file_info->FileName[0] != L'\\' ||
- file_info->FileName[1] != L'?' ||
- file_info->FileName[2] != L'?' ||
- file_info->FileName[3] != L'\\')
+ if (file_info->FileName[0] != kPathPrefix[0] ||
+ file_info->FileName[1] != kPathPrefix[1] ||
+ file_info->FileName[2] != kPathPrefix[2] ||
+ file_info->FileName[3] != kPathPrefix[3])
return false;
return true;
diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h
index a9f12451621..22a2049c2d1 100644
--- a/chromium/sandbox/win/src/sandbox_policy.h
+++ b/chromium/sandbox/win/src/sandbox_policy.h
@@ -26,7 +26,8 @@ class TargetPolicy {
SUBSYS_PROCESS, // Creation of child processes.
SUBSYS_REGISTRY, // Creation and opening of registry keys.
SUBSYS_SYNC, // Creation of named sync objects.
- SUBSYS_HANDLES // Duplication of handles to other processes.
+ SUBSYS_HANDLES, // Duplication of handles to other processes.
+ SUBSYS_WIN32K_LOCKDOWN // Win32K Lockdown related policy.
};
// Allowable semantics when a rule is matched.
@@ -52,7 +53,10 @@ class TargetPolicy {
EVENTS_ALLOW_ANY, // Allows the creation of an event with full access.
EVENTS_ALLOW_READONLY, // Allows opening an even with synchronize access.
REG_ALLOW_READONLY, // Allows readonly access to a registry key.
- REG_ALLOW_ANY // Allows read and write access to a registry key.
+ REG_ALLOW_ANY, // Allows read and write access to a registry key.
+ FAKE_USER_GDI_INIT // Fakes user32 and gdi32 initialization. This can
+ // be used to allow the DLLs to load and initialize
+ // even if the process cannot access that subsystem.
};
// Increments the reference count of this object. The reference count must
@@ -87,6 +91,12 @@ class TargetPolicy {
// as possible.
virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0;
+ // Returns the initial token level.
+ virtual TokenLevel GetInitialTokenLevel() const = 0;
+
+ // Returns the lockdown token level.
+ virtual TokenLevel GetLockdownTokenLevel() const = 0;
+
// Sets the security level of the Job Object to which the target process will
// belong. This setting is permanent and cannot be changed once the target
// process is spawned. The job controls the global security settings which
@@ -122,6 +132,11 @@ class TargetPolicy {
// Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN.
virtual ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) = 0;
+ // Sets a hard limit on the size of the commit set for the sandboxed process.
+ // If the limit is reached, the process will be terminated with
+ // SBOX_FATAL_MEMORY_EXCEEDED (7012).
+ virtual ResultCode SetJobMemoryLimit(size_t memory_limit) = 0;
+
// Specifies the desktop on which the application is going to run. If the
// desktop does not exist, it will be created. If alternate_winstation is
// set to true, the desktop will be created on an alternate window station.
@@ -144,6 +159,9 @@ class TargetPolicy {
// to start.
virtual ResultCode SetIntegrityLevel(IntegrityLevel level) = 0;
+ // Returns the initial integrity level used.
+ virtual IntegrityLevel GetIntegrityLevel() const = 0;
+
// Sets the integrity level of the process in the sandbox. The integrity level
// will not take effect before you call LowerToken. User Interface Privilege
// Isolation is not affected by this setting and will remain off for the
@@ -179,7 +197,7 @@ class TargetPolicy {
virtual ResultCode SetDelayedProcessMitigations(MitigationFlags flags) = 0;
// Returns the currently set delayed mitigation flags.
- virtual MitigationFlags GetDelayedProcessMitigations() = 0;
+ virtual MitigationFlags GetDelayedProcessMitigations() const = 0;
// Sets the interceptions to operate in strict mode. By default, interceptions
// are performed in "relaxed" mode, where if something inside NTDLL.DLL is
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc
index 220a0703024..711fafc006a 100644
--- a/chromium/sandbox/win/src/sandbox_policy_base.cc
+++ b/chromium/sandbox/win/src/sandbox_policy_base.cc
@@ -21,6 +21,8 @@
#include "sandbox/win/src/policy_broker.h"
#include "sandbox/win/src/policy_engine_processor.h"
#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/process_mitigations_win32k_dispatcher.h"
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
#include "sandbox/win/src/process_thread_dispatcher.h"
#include "sandbox/win/src/process_thread_policy.h"
#include "sandbox/win/src/registry_dispatcher.h"
@@ -80,6 +82,7 @@ PolicyBase::PolicyBase()
initial_level_(USER_LOCKDOWN),
job_level_(JOB_LOCKDOWN),
ui_exceptions_(0),
+ memory_limit_(0),
use_alternate_desktop_(false),
use_alternate_winstation_(false),
file_system_init_(false),
@@ -124,6 +127,11 @@ PolicyBase::PolicyBase()
dispatcher = new HandleDispatcher(this);
ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher;
+
+ dispatcher = new ProcessMitigationsWin32KDispatcher(this);
+ ipc_targets_[IPC_GDI_GDIDLLINITIALIZE_TAG] = dispatcher;
+ ipc_targets_[IPC_GDI_GETSTOCKOBJECT_TAG] = dispatcher;
+ ipc_targets_[IPC_USER_REGISTERCLASSW_TAG] = dispatcher;
}
PolicyBase::~PolicyBase() {
@@ -161,33 +169,52 @@ ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
return SBOX_ALL_OK;
}
+TokenLevel PolicyBase::GetInitialTokenLevel() const {
+ return initial_level_;
+}
+
+TokenLevel PolicyBase::GetLockdownTokenLevel() const{
+ return lockdown_level_;
+}
+
ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) {
+ if (memory_limit_ && job_level == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
job_level_ = job_level;
ui_exceptions_ = ui_exceptions;
return SBOX_ALL_OK;
}
+ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) {
+ if (memory_limit && job_level_ == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ memory_limit_ = memory_limit;
+ return SBOX_ALL_OK;
+}
+
ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
use_alternate_desktop_ = true;
use_alternate_winstation_ = alternate_winstation;
return CreateAlternateDesktop(alternate_winstation);
}
-string16 PolicyBase::GetAlternateDesktop() const {
+base::string16 PolicyBase::GetAlternateDesktop() const {
// No alternate desktop or winstation. Return an empty string.
if (!use_alternate_desktop_ && !use_alternate_winstation_) {
- return string16();
+ return base::string16();
}
// The desktop and winstation should have been created by now.
// If we hit this scenario, it means that the user ignored the failure
// during SetAlternateDesktop, so we ignore it here too.
if (use_alternate_desktop_ && !alternate_desktop_handle_) {
- return string16();
+ return base::string16();
}
if (use_alternate_winstation_ && (!alternate_desktop_handle_ ||
!alternate_winstation_handle_)) {
- return string16();
+ return base::string16();
}
return GetFullDesktopName(alternate_winstation_handle_,
@@ -265,6 +292,10 @@ ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
return SBOX_ALL_OK;
}
+IntegrityLevel PolicyBase::GetIntegrityLevel() const {
+ return integrity_level_;
+}
+
ResultCode PolicyBase::SetDelayedIntegrityLevel(
IntegrityLevel integrity_level) {
delayed_integrity_level_ = integrity_level;
@@ -316,7 +347,7 @@ ResultCode PolicyBase::SetDelayedProcessMitigations(
return SBOX_ALL_OK;
}
-MitigationFlags PolicyBase::GetDelayedProcessMitigations() {
+MitigationFlags PolicyBase::GetDelayedProcessMitigations() const {
return delayed_mitigations_;
}
@@ -401,6 +432,16 @@ ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics,
}
break;
}
+
+ case SUBSYS_WIN32K_LOCKDOWN: {
+ if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ pattern, semantics,policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+
default: {
return SBOX_ERROR_UNSUPPORTED;
}
@@ -414,8 +455,8 @@ ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
return SBOX_ALL_OK;
}
-ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type,
- const char16* handle_name) {
+ResultCode PolicyBase::AddKernelObjectToClose(const base::char16* handle_type,
+ const base::char16* handle_name) {
return handle_closer_.AddHandle(handle_type, handle_name);
}
@@ -459,7 +500,8 @@ ResultCode PolicyBase::MakeJobObject(HANDLE* job) {
if (job_level_ != JOB_NONE) {
// Create the windows job object.
Job job_obj;
- DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_);
+ DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_,
+ memory_limit_);
if (ERROR_SUCCESS != result) {
return SBOX_ERROR_GENERIC;
}
@@ -649,15 +691,12 @@ bool PolicyBase::SetupAllInterceptions(TargetProcess* target) {
}
if (!blacklisted_dlls_.empty()) {
- std::vector<string16>::iterator it = blacklisted_dlls_.begin();
+ std::vector<base::string16>::iterator it = blacklisted_dlls_.begin();
for (; it != blacklisted_dlls_.end(); ++it) {
manager.AddToUnloadModules(it->c_str());
}
}
- if (!handle_closer_.SetupHandleInterceptions(&manager))
- return false;
-
if (!SetupBasicInterceptions(&manager))
return false;
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.h b/chromium/sandbox/win/src/sandbox_policy_base.h
index d56501d78bc..540f261cdde 100644
--- a/chromium/sandbox/win/src/sandbox_policy_base.h
+++ b/chromium/sandbox/win/src/sandbox_policy_base.h
@@ -41,13 +41,17 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
virtual void Release() OVERRIDE;
virtual ResultCode SetTokenLevel(TokenLevel initial,
TokenLevel lockdown) OVERRIDE;
+ virtual TokenLevel GetInitialTokenLevel() const OVERRIDE;
+ virtual TokenLevel GetLockdownTokenLevel() const OVERRIDE;
virtual ResultCode SetJobLevel(JobLevel job_level,
uint32 ui_exceptions) OVERRIDE;
+ virtual ResultCode SetJobMemoryLimit(size_t memory_limit) OVERRIDE;
virtual ResultCode SetAlternateDesktop(bool alternate_winstation) OVERRIDE;
- virtual string16 GetAlternateDesktop() const OVERRIDE;
+ virtual base::string16 GetAlternateDesktop() const OVERRIDE;
virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) OVERRIDE;
virtual void DestroyAlternateDesktop() OVERRIDE;
virtual ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) OVERRIDE;
+ virtual IntegrityLevel GetIntegrityLevel() const OVERRIDE;
virtual ResultCode SetDelayedIntegrityLevel(
IntegrityLevel integrity_level) OVERRIDE;
virtual ResultCode SetAppContainer(const wchar_t* sid) OVERRIDE;
@@ -56,15 +60,16 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
virtual MitigationFlags GetProcessMitigations() OVERRIDE;
virtual ResultCode SetDelayedProcessMitigations(
MitigationFlags flags) OVERRIDE;
- virtual MitigationFlags GetDelayedProcessMitigations() OVERRIDE;
+ virtual MitigationFlags GetDelayedProcessMitigations() const OVERRIDE;
virtual void SetStrictInterceptions() OVERRIDE;
virtual ResultCode SetStdoutHandle(HANDLE handle) OVERRIDE;
virtual ResultCode SetStderrHandle(HANDLE handle) OVERRIDE;
virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics,
const wchar_t* pattern) OVERRIDE;
virtual ResultCode AddDllToUnload(const wchar_t* dll_name);
- virtual ResultCode AddKernelObjectToClose(const char16* handle_type,
- const char16* handle_name) OVERRIDE;
+ virtual ResultCode AddKernelObjectToClose(
+ const base::char16* handle_type,
+ const base::char16* handle_name) OVERRIDE;
// Dispatcher:
virtual Dispatcher* OnMessageReady(IPCParams* ipc,
@@ -123,6 +128,7 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
TokenLevel initial_level_;
JobLevel job_level_;
uint32 ui_exceptions_;
+ size_t memory_limit_;
bool use_alternate_desktop_;
bool use_alternate_winstation_;
// Helps the file system policy initialization.
@@ -141,12 +147,12 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
// Memory structure that stores the low level policy.
PolicyGlobal* policy_;
// The list of dlls to unload in the target process.
- std::vector<string16> blacklisted_dlls_;
+ std::vector<base::string16> blacklisted_dlls_;
// This is a map of handle-types to names that we need to close in the
// target process. A null set means we need to close all handles of the
// given type.
HandleCloser handle_closer_;
- std::vector<string16> capabilities_;
+ std::vector<base::string16> capabilities_;
scoped_ptr<AppContainerAttributes> appcontainer_list_;
static HDESK alternate_desktop_handle_;
diff --git a/chromium/sandbox/win/src/sandbox_types.h b/chromium/sandbox/win/src/sandbox_types.h
index 48f1b613f1e..22840cef077 100644
--- a/chromium/sandbox/win/src/sandbox_types.h
+++ b/chromium/sandbox/win/src/sandbox_types.h
@@ -58,6 +58,7 @@ enum TerminationCodes {
SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching.
SBOX_FATAL_CLOSEHANDLES = 7010, // Failed to close pending handles.
SBOX_FATAL_MITIGATION = 7011, // Could not set the mitigation policy.
+ SBOX_FATAL_MEMORY_EXCEEDED = 7012, // Exceeded the job memory limit.
SBOX_FATAL_LAST
};
diff --git a/chromium/sandbox/win/src/sandbox_utils.cc b/chromium/sandbox/win/src/sandbox_utils.cc
index 8631a7c9117..a1b77d65b68 100644
--- a/chromium/sandbox/win/src/sandbox_utils.cc
+++ b/chromium/sandbox/win/src/sandbox_utils.cc
@@ -7,18 +7,10 @@
#include <windows.h>
#include "base/logging.h"
-#include "base/win/windows_version.h"
#include "sandbox/win/src/internal_types.h"
namespace sandbox {
-bool IsXPSP2OrLater() {
- base::win::Version version = base::win::GetVersion();
- return (version > base::win::VERSION_XP) ||
- ((version == base::win::VERSION_XP) &&
- (base::win::OSInfo::GetInstance()->service_pack().major >= 2));
-}
-
void InitObjectAttribs(const base::string16& name,
ULONG attributes,
HANDLE root,
diff --git a/chromium/sandbox/win/src/sandbox_utils.h b/chromium/sandbox/win/src/sandbox_utils.h
index 3043597b185..2a17d63b8f9 100644
--- a/chromium/sandbox/win/src/sandbox_utils.h
+++ b/chromium/sandbox/win/src/sandbox_utils.h
@@ -14,9 +14,6 @@
namespace sandbox {
-// Returns true if the current OS is Windows XP SP2 or later.
-bool IsXPSP2OrLater();
-
void InitObjectAttribs(const base::string16& name,
ULONG attributes,
HANDLE root,
diff --git a/chromium/sandbox/win/src/security_level.h b/chromium/sandbox/win/src/security_level.h
index f8e72b3ce00..da84b75252b 100644
--- a/chromium/sandbox/win/src/security_level.h
+++ b/chromium/sandbox/win/src/security_level.h
@@ -76,7 +76,8 @@ enum TokenLevel {
USER_INTERACTIVE,
USER_NON_ADMIN,
USER_RESTRICTED_SAME_ACCESS,
- USER_UNPROTECTED
+ USER_UNPROTECTED,
+ USER_LAST
};
// The Job level specifies a set of decreasing security profiles for the
diff --git a/chromium/sandbox/win/src/service_resolver.h b/chromium/sandbox/win/src/service_resolver.h
index 00896922049..76daee40cb4 100644
--- a/chromium/sandbox/win/src/service_resolver.h
+++ b/chromium/sandbox/win/src/service_resolver.h
@@ -16,7 +16,7 @@ class ServiceResolverThunk : public ResolverThunk {
public:
// The service resolver needs a child process to write to.
ServiceResolverThunk(HANDLE process, bool relaxed)
- : process_(process), ntdll_base_(NULL), win2k_(false),
+ : process_(process), ntdll_base_(NULL),
relaxed_(relaxed), relative_jump_(0) {}
virtual ~ServiceResolverThunk() {}
@@ -46,6 +46,15 @@ class ServiceResolverThunk : public ResolverThunk {
// Call this to set up ntdll_base_ which will allow for local patches.
virtual void AllowLocalPatches();
+ // Verifies that the function specified by |target_name| in |target_module| is
+ // a service and copies the data from that function into |thunk_storage|. If
+ // |storage_bytes| is too small, then the method fails.
+ virtual NTSTATUS CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
protected:
// The unit test will use this member to allow local patch on a buffer.
HMODULE ntdll_base_;
@@ -53,10 +62,6 @@ class ServiceResolverThunk : public ResolverThunk {
// Handle of the child process.
HANDLE process_;
- protected:
- // Keeps track of a Windows 2000 resolver.
- bool win2k_;
-
private:
// Returns true if the code pointer by target_ corresponds to the expected
// type of function. Saves that code on the first part of the thunk pointed
@@ -114,23 +119,6 @@ class Wow64W8ResolverThunk : public ServiceResolverThunk {
};
// This is the concrete resolver used to perform service-call type functions
-// inside ntdll.dll on Windows 2000 and XP pre SP2.
-class Win2kResolverThunk : public ServiceResolverThunk {
- public:
- // The service resolver needs a child process to write to.
- Win2kResolverThunk(HANDLE process, bool relaxed)
- : ServiceResolverThunk(process, relaxed) {
- win2k_ = true;
- }
- virtual ~Win2kResolverThunk() {}
-
- private:
- virtual bool IsFunctionAService(void* local_thunk) const;
-
- DISALLOW_COPY_AND_ASSIGN(Win2kResolverThunk);
-};
-
-// This is the concrete resolver used to perform service-call type functions
// inside ntdll.dll on Windows 8.
class Win8ResolverThunk : public ServiceResolverThunk {
public:
diff --git a/chromium/sandbox/win/src/service_resolver_32.cc b/chromium/sandbox/win/src/service_resolver_32.cc
index 2e69dbc9e7e..be9de6b9224 100644
--- a/chromium/sandbox/win/src/service_resolver_32.cc
+++ b/chromium/sandbox/win/src/service_resolver_32.cc
@@ -179,6 +179,32 @@ size_t ServiceResolverThunk::GetThunkSize() const {
return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
}
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
ServiceEntry function_code;
SIZE_T read;
@@ -374,26 +400,6 @@ bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
return true;
}
-bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const {
- ServiceEntry function_code;
- SIZE_T read;
- if (!::ReadProcessMemory(process_, target_, &function_code,
- sizeof(function_code), &read))
- return false;
-
- if (sizeof(function_code) != read)
- return false;
-
- if (kMovEax != function_code.mov_eax ||
- function_code.service_id > kMaxService)
- return false;
-
- // Save the verified code
- memcpy(local_thunk, &function_code, sizeof(function_code));
-
- return true;
-}
-
bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
ServiceEntryW8 function_code;
SIZE_T read;
diff --git a/chromium/sandbox/win/src/service_resolver_64.cc b/chromium/sandbox/win/src/service_resolver_64.cc
index 473ddbc7f16..03795f7c9d0 100644
--- a/chromium/sandbox/win/src/service_resolver_64.cc
+++ b/chromium/sandbox/win/src/service_resolver_64.cc
@@ -56,7 +56,7 @@ struct ServiceEntryW8 {
ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
ULONG service_id;
USHORT syscall; // = 0F 05
- BYTE ret; // = C2
+ BYTE ret; // = C3
BYTE nop; // = 90
};
@@ -116,6 +116,30 @@ size_t ServiceResolverThunk::GetThunkSize() const {
return sizeof(ServiceFullThunk);
}
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_UNSUCCESSFUL;
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
ServiceFullThunk function_code;
SIZE_T read;
@@ -185,9 +209,4 @@ bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
return false;
}
-bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const {
- NOTREACHED_NT();
- return false;
-}
-
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/service_resolver_unittest.cc b/chromium/sandbox/win/src/service_resolver_unittest.cc
index b01fedfbf09..c7ac7eab169 100644
--- a/chromium/sandbox/win/src/service_resolver_unittest.cc
+++ b/chromium/sandbox/win/src/service_resolver_unittest.cc
@@ -58,7 +58,6 @@ class ResolverThunkTest : public T {
typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest;
#if !defined(_WIN64)
-typedef ResolverThunkTest<sandbox::Win2kResolverThunk> Win2kResolverTest;
typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest;
typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest;
@@ -141,9 +140,6 @@ sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) {
return new Wow64ResolverTest(relaxed);
}
- if (!sandbox::IsXPSP2OrLater())
- return new Win2kResolverTest(relaxed);
-
if (os_info->version() >= base::win::VERSION_WIN8)
return new Win8ResolverTest(relaxed);
diff --git a/chromium/sandbox/win/src/sharedmem_ipc_client.cc b/chromium/sandbox/win/src/sharedmem_ipc_client.cc
index a9eb01f36b2..9d37bbda7d4 100644
--- a/chromium/sandbox/win/src/sharedmem_ipc_client.cc
+++ b/chromium/sandbox/win/src/sharedmem_ipc_client.cc
@@ -31,7 +31,7 @@ void SharedMemIPCClient::FreeBuffer(void* buffer) {
size_t num = ChannelIndexFromBuffer(buffer);
ChannelControl* channel = control_->channels;
LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
- DCHECK(kFreeChannel != result);
+ DCHECK_NE(kFreeChannel, static_cast<ChannelState>(result));
result;
}
@@ -145,7 +145,7 @@ size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
size_t num = d/kIPCChannelSize;
- DCHECK(num < control_->channels_count);
+ DCHECK_LT(num, control_->channels_count);
return (num);
}
diff --git a/chromium/sandbox/win/src/sharedmem_ipc_server.cc b/chromium/sandbox/win/src/sharedmem_ipc_server.cc
index bf8761e8853..a1b881eb1b3 100644
--- a/chromium/sandbox/win/src/sharedmem_ipc_server.cc
+++ b/chromium/sandbox/win/src/sharedmem_ipc_server.cc
@@ -12,6 +12,11 @@
#include "sandbox/win/src/crosscall_params.h"
#include "sandbox/win/src/crosscall_server.h"
+namespace {
+// This handle must not be closed.
+volatile HANDLE g_alive_mutex = NULL;
+}
+
namespace sandbox {
SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process,
@@ -25,6 +30,19 @@ SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process,
target_process_id_(target_process_id),
target_job_object_(target_job),
call_dispatcher_(dispatcher) {
+ // We create a initially owned mutex. If the server dies unexpectedly,
+ // the thread that owns it will fail to release the lock and windows will
+ // report to the target (when it tries to acquire it) that the wait was
+ // abandoned. Note: We purposely leak the local handle because we want it to
+ // be closed by Windows itself so it is properly marked as abandoned if the
+ // server dies.
+ if (!g_alive_mutex) {
+ HANDLE mutex = ::CreateMutexW(NULL, TRUE, NULL);
+ if (::InterlockedCompareExchangePointer(&g_alive_mutex, mutex, NULL)) {
+ // We lost the race to create the mutex.
+ ::CloseHandle(mutex);
+ }
+ }
}
SharedMemIPCServer::~SharedMemIPCServer() {
@@ -108,15 +126,7 @@ bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size,
thread_provider_->RegisterWait(this, service_context->ping_event,
ThreadPingEventReady, service_context);
}
-
- // We create a mutex that the server locks. If the server dies unexpectedly,
- // the thread that owns it will fail to release the lock and windows will
- // report to the target (when it tries to acquire it) that the wait was
- // abandoned. Note: We purposely leak the local handle because we want it to
- // be closed by Windows itself so it is properly marked as abandoned if the
- // server dies.
- if (!::DuplicateHandle(::GetCurrentProcess(),
- ::CreateMutexW(NULL, TRUE, NULL),
+ if (!::DuplicateHandle(::GetCurrentProcess(), g_alive_mutex,
target_process_, &client_control_->server_alive,
SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, 0)) {
return false;
diff --git a/chromium/sandbox/win/src/target_process.cc b/chromium/sandbox/win/src/target_process.cc
index a2d630c9452..5f73adcd4f9 100644
--- a/chromium/sandbox/win/src/target_process.cc
+++ b/chromium/sandbox/win/src/target_process.cc
@@ -116,7 +116,7 @@ DWORD TargetProcess::Create(const wchar_t* exe_path,
exe_name_.reset(_wcsdup(exe_path));
// the command line needs to be writable by CreateProcess().
- scoped_ptr_malloc<wchar_t> cmd_line(_wcsdup(command_line));
+ scoped_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
// Start the target process suspended.
DWORD flags =
diff --git a/chromium/sandbox/win/src/target_process.h b/chromium/sandbox/win/src/target_process.h
index 0b72d98f231..9a8dded55a2 100644
--- a/chromium/sandbox/win/src/target_process.h
+++ b/chromium/sandbox/win/src/target_process.h
@@ -113,7 +113,7 @@ class TargetProcess {
// Base address of the main executable
void* base_address_;
// Full name of the target executable.
- scoped_ptr_malloc<wchar_t> exe_name_;
+ scoped_ptr<wchar_t, base::FreeDeleter> exe_name_;
// Function used for testing.
friend TargetProcess* MakeTestTargetProcess(HANDLE process,
diff --git a/chromium/sandbox/win/src/win_utils.cc b/chromium/sandbox/win/src/win_utils.cc
index 53a12a4f292..ea68c07f35b 100644
--- a/chromium/sandbox/win/src/win_utils.cc
+++ b/chromium/sandbox/win/src/win_utils.cc
@@ -7,6 +7,7 @@
#include <map>
#include "base/memory/scoped_ptr.h"
+#include "base/win/pe_image.h"
#include "sandbox/win/src/internal_types.h"
#include "sandbox/win/src/nt_internals.h"
#include "sandbox/win/src/sandbox_nt_util.h"
@@ -114,7 +115,7 @@ DWORD IsReparsePoint(const base::string16& full_path, bool* result) {
}
last_pos = path.rfind(L'\\');
- } while (last_pos != base::string16::npos);
+ } while (last_pos > 2); // Skip root dir.
*result = false;
return ERROR_SUCCESS;
@@ -299,26 +300,22 @@ bool WriteProtectedChildMemory(HANDLE child_process, void* address,
}; // namespace sandbox
-// TODO(jschuh): http://crbug.com/11789
-// I'm guessing we have a race where some "security" software is messing
-// with ntdll/imports underneath us. So, we retry a few times, and in the
-// worst case we sleep briefly before a few more attempts. (Normally sleeping
-// would be very bad, but it's better than crashing in this case.)
void ResolveNTFunctionPtr(const char* name, void* ptr) {
- const int max_tries = 5;
- const int sleep_threshold = 2;
+ static volatile HMODULE ntdll = NULL;
- static HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName);
+ if (!ntdll) {
+ HMODULE ntdll_local = ::GetModuleHandle(sandbox::kNtdllName);
+ // Use PEImage to sanity-check that we have a valid ntdll handle.
+ base::win::PEImage ntdll_peimage(ntdll_local);
+ CHECK_NT(ntdll_peimage.VerifyMagic());
+ // Race-safe way to set static ntdll.
+ ::InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, NULL);
- FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
- *function_ptr = ::GetProcAddress(ntdll, name);
-
- for (int tries = 1; !(*function_ptr) && tries < max_tries; ++tries) {
- if (tries >= sleep_threshold)
- ::Sleep(1);
- ntdll = ::GetModuleHandle(sandbox::kNtdllName);
- *function_ptr = ::GetProcAddress(ntdll, name);
}
+ CHECK_NT(ntdll);
+ FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
+ *function_ptr = ::GetProcAddress(ntdll, name);
CHECK_NT(*function_ptr);
}
diff --git a/chromium/sandbox/win/src/window.cc b/chromium/sandbox/win/src/window.cc
index 6b5766b325b..ed5a6626338 100644
--- a/chromium/sandbox/win/src/window.cc
+++ b/chromium/sandbox/win/src/window.cc
@@ -48,9 +48,8 @@ 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;
}
@@ -100,11 +99,14 @@ ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* 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;
+ static const ACCESS_MASK kDesktopDenyMask = WRITE_DAC | WRITE_OWNER |
+ DELETE |
+ DESKTOP_CREATEMENU |
+ DESKTOP_CREATEWINDOW |
+ DESKTOP_HOOKCONTROL |
+ DESKTOP_JOURNALPLAYBACK |
+ DESKTOP_JOURNALRECORD |
+ DESKTOP_SWITCHDESKTOP;
AddKnownSidToObject(*desktop, SE_WINDOW_OBJECT, Sid(WinRestrictedCodeSid),
DENY_ACCESS, kDesktopDenyMask);
return SBOX_ALL_OK;
diff --git a/chromium/sandbox/win/wow_helper/wow_helper.cc b/chromium/sandbox/win/wow_helper/wow_helper.cc
index 847190ac8b8..ea73e8487c2 100644
--- a/chromium/sandbox/win/wow_helper/wow_helper.cc
+++ b/chromium/sandbox/win/wow_helper/wow_helper.cc
@@ -15,43 +15,6 @@
#include "sandbox/win/wow_helper/service64_resolver.h"
#include "sandbox/win/wow_helper/target_code.h"
-namespace {
-
-// Grabbed from base/strings/string_util.h
-template <class string_type>
-inline typename string_type::value_type* WriteInto(string_type* str,
- size_t length_with_null) {
- str->reserve(length_with_null);
- str->resize(length_with_null - 1);
- return &((*str)[0]);
-}
-
-// Grabbed from base/string_util.cc
-std::string WideToMultiByte(const base::string16& wide, UINT code_page) {
- if (wide.length() == 0)
- return std::string();
-
- // compute the length of the buffer we'll need
- int charcount = WideCharToMultiByte(code_page, 0, wide.c_str(), -1,
- NULL, 0, NULL, NULL);
- if (charcount == 0)
- return std::string();
-
- // convert
- std::string mb;
- WideCharToMultiByte(code_page, 0, wide.c_str(), -1,
- WriteInto(&mb, charcount), charcount, NULL, NULL);
-
- return mb;
-}
-
-// Grabbed from base/string_util.cc
-std::string WideToUTF8(const base::string16& wide) {
- return WideToMultiByte(wide, CP_UTF8);
-}
-
-} // namespace
-
namespace sandbox {
// Performs the interception of NtMapViewOfSection on the 64-bit version of